192 Commits

Author SHA1 Message Date
Doug Rios
cd3162aad0 Merge pull request #110 from CriticalSolutionsNetwork/Add-condition-comments-to-tests
Add condition comments to tests
2024-06-12 13:10:00 -05:00
DrIOS
9dba7c2be8 docs: Update Changelog 2024-06-12 13:00:23 -05:00
DrIOS
b123bd4155 docs: Comment conditions on each test 2024-06-12 12:58:38 -05:00
DrIOS
10471b4683 docs: Comment conditions on each test 2024-06-12 12:24:25 -05:00
DrIOS
cf7fbadbe7 docs: Comment conditions on each test 2024-06-11 18:41:19 -05:00
DrIOS
e6b6e064bf docs: Comment conditions on each test 2024-06-11 13:03:59 -05:00
Doug Rios
f85101d0de Merge pull request #108 from CriticalSolutionsNetwork/release-branch
fix: working and verbose confirmation included
2024-06-10 13:00:33 -05:00
DrIOS
f880e566ea fix: working and verbose confirmation included 2024-06-10 12:58:50 -05:00
Doug Rios
7041b0ba52 Merge pull request #107 from CriticalSolutionsNetwork/Bugfix-1.1.1
Bugfix 1.1.1
2024-06-10 12:55:48 -05:00
DrIOS
1161baffad fix: working and verbose confirmation included 2024-06-10 12:31:22 -05:00
DrIOS
032c951e02 fix: working but needs tuning 2024-06-10 11:55:19 -05:00
DrIOS
6ed99dbacf fix: Comments steps 2024-06-10 09:56:42 -05:00
DrIOS
30c848e74d fix: Revert script to oringinal for 1.1.1 2024-06-10 09:42:17 -05:00
DrIOS
40193bd492 docs: Update git issue build 2024-06-09 14:06:34 -05:00
DrIOS
5c868a20fc docs: Fomatting changes 2024-06-09 10:54:34 -05:00
Doug Rios
4db0fd3742 Merge pull request #100 from CriticalSolutionsNetwork/Whatif-Bugfix
fix: whatif
2024-06-09 10:42:00 -05:00
DrIOS
83a8e31aa5 docs: Update CHANGELOG 2024-06-09 10:38:56 -05:00
DrIOS
b9de0638bb add: Output type to functions 2024-06-09 10:36:37 -05:00
DrIOS
5a0475c253 docs: update CHANGELOG.md 2024-06-09 09:50:55 -05:00
DrIOS
312aabc81c fix: whatif output and module install 2024-06-09 09:40:18 -05:00
DrIOS
e6da6d9d47 fix: whatif 2024-06-08 20:42:38 -05:00
Doug Rios
014c42b3fe Merge pull request #19 from CriticalSolutionsNetwork/Make-tenant-admin-optional
Make tenant admin optional
2024-06-08 19:32:55 -05:00
DrIOS
fbfb5b5986 add: build help for issues 2024-06-08 19:31:29 -05:00
DrIOS
03b5bb47e2 docs: Update HelpE 2024-06-08 18:12:01 -05:00
DrIOS
9dc99636d3 fix: module check included for whatif 2024-06-08 17:57:42 -05:00
DrIOS
afe657ffc0 fix: module check included for whatif 2024-06-08 17:48:43 -05:00
DrIOS
702f557579 fix: module check included for whatif 2024-06-08 17:45:31 -05:00
DrIOS
f855ef7d0b fix: Update supports should process for connection/disconect 2024-06-08 17:44:16 -05:00
DrIOS
270e980a57 docs: Update CHANGELOG 2024-06-08 17:41:23 -05:00
DrIOS
ff90669984 fix: Update supports should process for connection/disconect 2024-06-08 17:41:09 -05:00
DrIOS
f2e799af2f docs: Update HelpE 2024-06-08 17:31:28 -05:00
DrIOS
4a4d200197 fix: throw error if no test definitioins after SPO removal 2024-06-08 17:26:30 -05:00
DrIOS
9199d97fc2 docs: Update Help and README 2024-06-08 17:22:39 -05:00
DrIOS
5d681f3d72 docs: update CHANGELOG 2024-06-08 17:19:39 -05:00
DrIOS
f926c63533 add: tenantadmin url as optional parameter 2024-06-08 17:19:22 -05:00
Doug Rios
d5044f0bf4 Merge pull request #18 from CriticalSolutionsNetwork/Sync-function-pipeline-input-support
Sync function pipeline input support
2024-06-08 16:42:46 -05:00
DrIOS
055ab42261 docs: Update docs/README 2024-06-08 16:40:58 -05:00
DrIOS
0d97b95c6c docs: Update changelog 2024-06-08 16:38:24 -05:00
DrIOS
c185878674 add: pipeline input for CISAuditResult object input to Sync function 2024-06-08 16:35:00 -05:00
DrIOS
61063ee63c Revert "Rename powershell.yml to powershell.yml.bakcup"
This reverts commit 4115f1e83e.
2024-06-08 16:23:02 -05:00
Doug Rios
4115f1e83e Rename powershell.yml to powershell.yml.bakcup 2024-06-08 16:20:08 -05:00
Doug Rios
0b3213d957 Merge pull request #17 from CriticalSolutionsNetwork/Fix-domain-test
Fix domain test
2024-06-08 15:58:29 -05:00
DrIOS
9d9b9e70d9 docs: update help 2024-06-08 15:56:25 -05:00
DrIOS
4167a37121 docs: Update CHANGELOG.md 2024-06-08 15:54:48 -05:00
DrIOS
66536e34a7 fix: update domain pw policy logic 2024-06-08 15:53:12 -05:00
DrIOS
db9b206ae3 docs: Update help 2024-06-08 15:00:10 -05:00
DrIOS
8a9044486b docs: Update Help and README 2024-06-08 14:56:51 -05:00
Doug Rios
447be9cacb Merge pull request #12 from CriticalSolutionsNetwork/Better-Error-Handling
Better error handling
2024-06-08 14:52:15 -05:00
Doug Rios
71c798c52a Merge pull request #16 from CriticalSolutionsNetwork/main
Create SECURITY.md
2024-06-08 14:40:38 -05:00
Doug Rios
d0270027f9 Create SECURITY.md 2024-06-08 14:39:13 -05:00
DrIOS
0569fd98cc test: update workflow 2024-06-08 14:36:41 -05:00
DrIOS
c842ae9720 test: update workflow 2024-06-08 14:32:43 -05:00
DrIOS
63b9e8b75d test: update workflow 2024-06-08 14:28:19 -05:00
DrIOS
f7b87ebc78 test: update workflow 2024-06-08 14:25:00 -05:00
DrIOS
b70da1845f addL paramter validation to main function 2024-06-08 14:10:55 -05:00
DrIOS
ef55447e67 docs:update help 2024-06-08 14:03:55 -05:00
DrIOS
7a9d2885f3 fix: script analyzer reccomendations. 2024-06-08 13:29:48 -05:00
DrIOS
4ab5affc9f fix: script analyzer reccomendations. 2024-06-08 13:26:13 -05:00
DrIOS
5871294210 add: progress bar and surpressed verbose progress. 2024-06-08 13:01:39 -05:00
DrIOS
a5dc7f1ebd docs: update help 2024-06-08 12:38:47 -05:00
DrIOS
c0222ef3bc add:comment-help block back to public function 2024-06-08 12:11:01 -05:00
DrIOS
83ee6c2ac3 add: New private function for csv/xlsx merge 2024-06-08 12:02:16 -05:00
DrIOS
cbdb31c7c5 add: better output to 1.1.3 2024-06-08 11:54:54 -05:00
DrIOS
a5d26917d3 add: paramters to get-requredmodules and updated public functions 2024-06-08 11:51:54 -05:00
DrIOS
4f8df29c72 add: Output type to tests. 2024-06-08 11:35:25 -05:00
DrIOS
bb1df11128 add: Output type to tests. 2024-06-08 11:34:21 -05:00
DrIOS
2d9a1a1d10 docs: Update CHANGELOG 2024-06-08 11:32:10 -05:00
DrIOS
41dbf2f0db add: Output type to tests. 2024-06-08 11:31:40 -05:00
DrIOS
70dcd74643 docs: Update CHANGELOG 2024-06-08 11:28:13 -05:00
DrIOS
f6aa4b83dd add: parameter validation for excel and csv path in sync function 2024-06-08 11:27:34 -05:00
DrIOS
d58d0b664d docs: Update CHANGELOG 2024-06-08 11:25:47 -05:00
DrIOS
b9a8a75945 fix: minimized mggraph call count and adjusted output in 1.1.1 2024-06-08 11:20:02 -05:00
DrIOS
6495073a10 fix: step 1 and step 2 in 6.2.1 details. 2024-06-08 10:39:29 -05:00
DrIOS
e0e2a04b6a fix: add check for length in 7.3.4 details. 2024-06-08 09:13:46 -05:00
DrIOS
d85968935b fix: remove verbose preference from 7.3.4 2024-06-07 21:37:28 -05:00
DrIOS
f47efa74f9 docs: Update changelog 2024-06-07 21:20:24 -05:00
DrIOS
f905f269d1 fix: output for 6.1.2/3 and 7.3.4 2024-06-07 21:10:28 -05:00
DrIOS
8719900af7 test: testingoutput 2024-06-07 20:49:37 -05:00
DrIOS
8922ea12cd fix: improve output of 7.3.4 2024-06-07 18:08:15 -05:00
DrIOS
c6bdad0477 fix: improve output of 6.1.2 2024-06-07 18:07:36 -05:00
DrIOS
85bd0fb19f add: sort to output 2024-06-07 18:06:47 -05:00
DrIOS
8de61dda9f fix: 7.3.4 so it outputs a pipe separated table 2024-06-07 14:15:57 -05:00
DrIOS
c530d2df76 docs: Update CHANGELOG 2024-06-07 13:39:12 -05:00
DrIOS
f5ab68dd63 fix: output in 6.2.1 so joine won't fail 2024-06-07 13:04:33 -05:00
DrIOS
d9ed3b60b7 fix: output in 6.1.2,6.1.3 update changelog 2024-06-07 10:31:38 -05:00
DrIOS
4857aead5e docs: update changelog 2024-06-06 16:56:58 -05:00
DrIOS
92e5952f7e Add: Module function 2024-06-06 16:40:18 -05:00
DrIOS
cb52ce1a73 docs: update CHANGELOG 2024-06-06 14:43:02 -05:00
DrIOS
4bc1f8fdda change: refactored test output and created new functions 2024-06-06 14:38:19 -05:00
DrIOS
8446d101a7 fix: connections in helper csv and connect function 2024-06-05 17:40:45 -05:00
DrIOS
06aeadaee5 fix: connections in helper csv and connect function 2024-06-05 15:43:45 -05:00
DrIOS
a9e44a7c6f fix: Removed nested functions in 6.1.2/6.1.3 2024-06-05 15:07:04 -05:00
DrIOS
2712f78412 docs: update CHANGELOG 2024-06-05 15:05:21 -05:00
DrIOS
df89e23bc1 add: simplified object build call 2024-06-05 14:07:19 -05:00
DrIOS
2027e8b21b Add: error handling to tests 2024-06-04 17:04:18 -05:00
Doug Rios
5c60f39dad Merge pull request #11 from CriticalSolutionsNetwork/Add-additional-properties-to-merged-doc
Add additional properties to merged doc / refactor connections and filtering of tests.
2024-05-30 16:55:56 -05:00
DrIOS
399288b10a add: add score to console host 2024-05-30 16:41:38 -05:00
DrIOS
87d0aaaea6 docs: Update CHANGELOG 2024-05-30 16:21:23 -05:00
DrIOS
8301b8cdbe add: Logging functions for future use 2024-05-30 16:19:05 -05:00
DrIOS
3f4166e98a add: refactor for connect/disconnect 2024-05-30 16:18:47 -05:00
DrIOS
7582511dd5 add: filter function for tests 2024-05-30 15:03:59 -05:00
DrIOS
20afb8d83e add: connection property to merge 2024-05-30 12:10:14 -05:00
Doug Rios
c378f5d119 Create powershell.yml 2024-05-28 21:19:28 -05:00
DrIOS
3a37d465e8 docs: Update Readme and Changelog 2024-05-28 20:46:23 -05:00
Doug Rios
531cedc0f6 Merge pull request #5 from CriticalSolutionsNetwork/test-arraylist
Refactor and Align Tests with selected template
2024-05-28 20:12:30 -05:00
DrIOS
54f9e086a9 docs: Update README 2024-05-28 20:10:54 -05:00
DrIOS
d1555e1d25 docs: update help 2024-05-28 20:07:46 -05:00
DrIOS
ac8014ae77 docs: update CHANGELOG 2024-05-28 19:53:15 -05:00
DrIOS
fc9923b980 docs: updated online help link 2024-05-28 19:52:47 -05:00
DrIOS
290540b25a fix: Refactor Initialize-CISAuditResult to use global test definitions 2024-05-28 19:25:27 -05:00
DrIOS
b064f9f271 add: refactor of object initialization 2024-05-28 18:50:18 -05:00
DrIOS
cafcaf2671 add: missing properties to csv 2024-05-28 17:13:00 -05:00
DrIOS
129bb33a99 add: New testing function 2024-05-28 17:08:04 -05:00
DrIOS
8505439516 fix: class comment aligned with test-template 2024-05-28 14:54:32 -05:00
DrIOS
686272d4e0 fix: 8.1.1 aligned with test-template 2024-05-28 14:51:24 -05:00
DrIOS
f445893aed fix: 8.2.1 aligned with test-template 2024-05-28 14:49:23 -05:00
DrIOS
050ea83acd fix: 2.1.6 aligned with test-template 2024-05-28 14:47:42 -05:00
DrIOS
deec4c4f5e fix: 7.2.5 aligned with test-template 2024-05-28 14:43:30 -05:00
DrIOS
6930673209 fix: 7.2.6 aligned with test-template 2024-05-28 14:40:56 -05:00
DrIOS
87db439d66 fix: 7.2.2 aligned with test-template 2024-05-28 14:38:28 -05:00
DrIOS
3d84a86793 fix: 2.1.1 aligned with test-template 2024-05-28 14:34:17 -05:00
DrIOS
dbc577bc67 fix: 2.1.5 aligned with test-template 2024-05-28 14:29:09 -05:00
DrIOS
d1a5cb8d73 fix: 2.1.4 aligned with test-template 2024-05-28 14:26:38 -05:00
DrIOS
c1e94ff3bc fix: 5.1.2.3 aligned with test-template 2024-05-28 14:24:09 -05:00
DrIOS
532cb942e8 fix: 6.5.3 aligned with test-template 2024-05-28 14:20:06 -05:00
DrIOS
2d93422027 fix: 6.3.1 aligned with test-template 2024-05-28 14:17:12 -05:00
DrIOS
75faf04ea6 fix: 7.2.3 aligned with test-template 2024-05-28 13:27:15 -05:00
DrIOS
c7cdaa4bf6 fix: 7.3.4 aligned with test-template 2024-05-28 13:24:10 -05:00
DrIOS
d511ea7b27 fix: 8.6.1 aligned with test-template 2024-05-28 13:21:39 -05:00
DrIOS
5b3c68a8f9 fix: 7.2.10 aligned with test-template 2024-05-28 13:19:20 -05:00
DrIOS
3883e33a4a fix: 1.3.1 aligned with test-template 2024-05-28 13:17:11 -05:00
DrIOS
398ce397f5 fix: 5.1.8.1 aligned with test-template 2024-05-28 13:14:34 -05:00
DrIOS
776b0bf2ec fix: 8.5.3 aligned with test-template 2024-05-28 13:12:38 -05:00
DrIOS
8d03d1825b fix: 8.5.6 aligned with test-template 2024-05-28 13:10:32 -05:00
DrIOS
26fa3a8922 fix: 7.3.2 aligned with test-template 2024-05-28 13:08:36 -05:00
DrIOS
c918f0203e fix: 7.2.4 aligned with test-template 2024-05-28 11:31:45 -05:00
DrIOS
c752b7e4fd fix: 6.2.2 aligned with test-template 2024-05-28 11:29:07 -05:00
DrIOS
3cf76bb6b3 fix: 2.1.3 aligned with test-template 2024-05-28 11:25:57 -05:00
DrIOS
36cb3f1944 fix: 8.5.2 aligned with test-template 2024-05-28 11:23:55 -05:00
DrIOS
c122174cb7 fix: 8.5.1 aligned with test-template 2024-05-28 11:22:23 -05:00
DrIOS
d5c64910ab fix: 7.2.1 aligned with test-template 2024-05-28 11:15:22 -05:00
DrIOS
77c74432fe fix: 6.5.1 aligned with test-template 2024-05-28 11:13:37 -05:00
DrIOS
b4c0993240 fix: 8.5.5 aligned with test-template 2024-05-28 11:09:30 -05:00
DrIOS
e96a5a9d48 fix: 1.2.1 aligned with test-template 2024-05-28 11:05:41 -05:00
DrIOS
2148a37b35 fix: 6.5.2 aligned with test-template 2024-05-28 11:02:25 -05:00
DrIOS
0196607f69 fix: 6.1.2/3 aligned with test-template 2024-05-28 10:59:23 -05:00
DrIOS
f5a3f0e460 fix: 6.1.2 aligned with test-template 2024-05-28 10:54:52 -05:00
DrIOS
5ed1e1de35 fix: 7.2.7 aligned with test-template 2024-05-28 10:49:31 -05:00
DrIOS
0d049c6dcb fix: 6.2.3 aligned with test-template 2024-05-28 10:44:16 -05:00
DrIOS
4b28258b3e fix: added test comments 2024-05-28 10:41:57 -05:00
DrIOS
b8690ddb44 fix: 1.1.4 aligned with test-template 2024-05-28 10:39:54 -05:00
DrIOS
dc88df6eef fix: 7.2.9 aligned with test-template 2024-05-28 10:36:29 -05:00
DrIOS
d0c3d907b9 fix: 1.1.3 aligned with test-template 2024-05-28 10:33:54 -05:00
DrIOS
20ee994ebc fix: 1.3.3 aligned with test-template 2024-05-28 10:31:06 -05:00
DrIOS
0d764e6152 fix: 8.5.7 aligned with test-template 2024-05-28 10:27:38 -05:00
DrIOS
0ae69e5b56 fix: 2.1.9 aligned with test-template 2024-05-28 10:25:03 -05:00
DrIOS
ccb02d84d2 fix: 7.3.1 added comment 2024-05-28 10:24:34 -05:00
DrIOS
5f4217d264 fix: 7.3.1 aligned with test-template 2024-05-28 10:15:40 -05:00
DrIOS
5e9fbfd690 fix: 8.5.4 aligned with test-template 2024-05-28 10:09:58 -05:00
DrIOS
fbe22abe9c fix: 1.3.6 aligned with test-template 2024-05-28 10:05:35 -05:00
DrIOS
283b278524 fix: 2.1.2 aligned with test-template 2024-05-28 09:58:49 -05:00
DrIOS
2f8d7b358a fix: 1.2.2 aligned with test-template 2024-05-28 09:55:57 -05:00
DrIOS
b93df00334 fix: "6.2.1" aligned with test-template 2024-05-28 09:52:30 -05:00
DrIOS
d5faf071b9 fix: "8.1.2" aligned with test-template 2024-05-28 09:39:42 -05:00
DrIOS
a1a2ecbd49 fix: Included functions aligned. 2024-05-28 09:29:06 -05:00
DrIOS
f4ae24b99f add: connections to helper table 2024-05-26 17:32:08 -05:00
DrIOS
652e5ead75 add: aligned test-AuditDisabledFalse 2024-05-26 16:28:04 -05:00
DrIOS
71736bfb43 fix: AntiPhishingPolicy aligned with template 2024-05-26 15:53:57 -05:00
DrIOS
4bc75db1dc add: Details to pass. 2024-05-26 15:34:16 -05:00
DrIOS
37fbf2b32a add: Correction to test-template 2024-05-26 15:33:57 -05:00
DrIOS
05dbf4661c add: Arraylist tests and helper Template 2024-05-26 14:36:27 -05:00
DrIOS
5f5b220fd4 add: Array list to store the results of the audit 2024-05-26 13:10:06 -05:00
Doug Rios
d06875700d Merge pull request #4 from CriticalSolutionsNetwork/Add-manual-checks-as-automated-checks
Enhancements to CSV Testing and Excel Benchmark Integration
2024-04-29 11:17:10 -05:00
DrIOS
822e2f51a3 docs: Update help documentation 2024-04-29 11:03:41 -05:00
DrIOS
cee453a8eb docs: Update Changelog and automation candidates 2024-04-29 11:01:42 -05:00
DrIOS
a8c7da2b7d fix: add project uri and icon to manifest. 2024-04-16 13:33:19 -05:00
DrIOS
02529c9cba docs: Update help 2024-04-16 09:50:29 -05:00
DrIOS
bbb1dd3586 fix: Properties for skip and include tests 2024-04-16 09:04:14 -05:00
DrIOS
8e2fab701c docs: Update comments for new functions 2024-04-15 17:03:39 -05:00
DrIOS
8f44424962 fix: Error handling in 1.1.1 test 2024-04-15 16:27:01 -05:00
DrIOS
fe503509ea fix: Test-AdministrativeAccountCompliance filename 2024-04-15 16:02:13 -05:00
DrIOS
ad2c85d034 fix: format TestDefinitions.csv 2024-04-15 15:58:12 -05:00
DrIOS
b381421f45 add: Error handling for Get-AdminRoleUserLicense 2024-04-15 15:54:52 -05:00
DrIOS
a4dce52825 add: future changes to be committed 2024-04-15 15:51:00 -05:00
DrIOS
d037f82f60 add: test for 1.1.4 guest users 2024-04-08 13:59:35 -05:00
DrIOS
ba0a3819b9 add: Create public function for merge 2024-04-05 18:35:21 -05:00
DrIOS
1e510f311b add: Functions to merge tests into excel benchmark 2024-04-05 18:13:16 -05:00
DrIOS
d033d7ae1b add: 1.1.1 test as automated and organized csv. 2024-04-05 16:13:08 -05:00
Doug Rios
90b34efa1b Merge pull request #3 from CriticalSolutionsNetwork/Fix-Test-ModernAuthExchange
Fix: Test-ModernAuthExchangeOnline and add notes.
2024-04-02 10:34:09 -05:00
DrIOS
a2020ff5ce Fix: Test-ModernAuthExchangeOnline and add notes. 2024-04-02 10:32:19 -05:00
Doug Rios
690b1c4667 Update README.md 2024-04-02 09:10:21 -05:00
Doug Rios
21bf1cbab8 Update README.md
Add link to benchmarks.
2024-04-02 09:09:49 -05:00
103 changed files with 5802 additions and 1798 deletions

50
.github/workflows/powershell.yml vendored Normal file
View File

@@ -0,0 +1,50 @@
# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.
#
# https://github.com/microsoft/action-psscriptanalyzer
# For more information on PSScriptAnalyzer in general, see
# https://github.com/PowerShell/PSScriptAnalyzer
name: PSScriptAnalyzer
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
schedule:
- cron: '33 20 * * 4'
permissions:
contents: read
jobs:
build:
permissions:
contents: read # for actions/checkout to fetch code
security-events: write # for github/codeql-action/upload-sarif to upload SARIF results
actions: read # only required for a private repository by github/codeql-action/upload-sarif to get the Action run status
name: PSScriptAnalyzer
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run PSScriptAnalyzer
uses: microsoft/psscriptanalyzer-action@6b2948b1944407914a58661c49941824d149734f
with:
# Check https://github.com/microsoft/action-psscriptanalyzer for more info about the options.
# The below set up runs PSScriptAnalyzer to your entire repository and runs some basic security rules.
path: .\source
recurse: true
# Include your own basic security rules. Removing this option will run all the rules
includeRule: '"PSAvoidGlobalAliases", "PSAvoidUsingConvertToSecureStringWithPlainText", "PSAvoidUsingPlainTextForPassword", "PSAvoidUsingInvokeExpression", "PSUseApprovedVerbs", "PSAvoidUsingPositionalParameters", "PSAvoidUsingEmptyCatchBlock", "PSAvoidUsingDeprecatedManifestFields", "PSAvoidUsingUserNameAndPasswordParams", "PSAvoidUsingCmdletAliases"'
output: results.sarif
# Upload the SARIF file generated in the previous step
- name: Upload SARIF results file
uses: github/codeql-action/upload-sarif@v2
with:
sarif_file: results.sarif

1
.gitignore vendored
View File

@@ -15,3 +15,4 @@ output/
markdownissues.txt markdownissues.txt
node_modules node_modules
package-lock.json package-lock.json
Aligned.xlsx

5
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,5 @@
{
"cSpell.words": [
"Msol"
]
}

View File

@@ -6,6 +6,163 @@ The format is based on and uses the types of changes according to [Keep a Change
### Added ### Added
- Added condition comments to each test.
### Fixed
- Fixed csv CIS controls that were not matched correctly.
## [0.1.9] - 2024-06-10
### Fixed
- Fixed bug in 1.1.1 that caused the test to fail/pass incorrectly. Added verbose output.
### Docs
- Updated helper csv formatting for one cis control.
## [0.1.8] - 2024-06-09
### Added
- Added output type to functions.
### Fixed
- Whatif support for `Invoke-M365SecurityAudit`.
- Whatif module output and module install process.
## [0.1.7] - 2024-06-08
### Added
- Added pipeline support to `Sync-CISExcelAndCsvData` function for `[CISAuditResult[]]` input.
### Changed
- Updated `Connect-M365Suite` to make `TenantAdminUrl` an optional parameter.
- Updated `Invoke-M365SecurityAudit` to make `TenantAdminUrl` an optional parameter.
- Improved connection handling and error messaging in `Connect-M365Suite`.
- Enhanced `Invoke-M365SecurityAudit` to allow flexible inclusion and exclusion of specific recommendations, IG filters, and profile levels.
- SupportsShoudProcess to also bypass connection checks in `Invoke-M365SecurityAudit` as well as Disconnect-M365Suite.
## [0.1.6] - 2024-06-08
### Added
- Added pipeline support to `Sync-CISExcelAndCsvData` function for `[CISAuditResult[]]` input.
## [0.1.5] - 2024-06-08
### Added
- Updated test definitions for CIS Microsoft 365 Foundations Benchmark for better error handling and object output when errors occur.
- Added a parameter to the `Initialize-CISAuditResult` function to allow for a static failed object to be created when an error occurs.
- Refactored `Invoke-M365SecurityAudit` to include a new private function `Invoke-TestFunction` for executing test functions and handling errors.
- Added a new private function `Measure-AuditResult` to calculate and display audit results.
- Enhanced error logging to capture failed test details and display them at the end of the audit.
- Added a private function `Get-RequiredModule` to initialize the `$requiredModules` variable for better code organization in the main script.
- Updated `Test-MailboxAuditingE3` and `Test-MailboxAuditingE5` functions to use `Format-MissingAction` for structuring missing actions into a pipe-separated table format.
- Added more verbose logging to `Test-BlockMailForwarding` and improved error handling for better troubleshooting.
- Improved `Test-RestrictCustomScripts` to handle long URL lengths better by extracting and replacing common hostnames, and provided detailed output.
- Added sorting to output.
- Created new functions for improved modularity.
- Parameter validation for Excel and CSV path in sync function.
- Added Output type to tests.
- Added `M365DomainForPWPolicyTest` parameter to `Invoke-M365SecurityAudit` to specify testing only the default domain for password expiration policy when '1.3.1' is included in the tests.
### Fixed
- Ensured the `Invoke-TestFunction` returns a `CISAuditResult` object, which is then managed in the `Invoke-M365SecurityAudit` function.
- Corrected the usage of the join operation within `$details` in `Test-BlockMailForwarding` to handle arrays properly.
- Fixed the logic in `Test-RestrictCustomScripts` to accurately replace and manage URLs, ensuring compliance checks are correctly performed.
- Updated the `Test-MailboxAuditingE3` and `Test-MailboxAuditingE5` functions to handle the `$allFailures` variable correctly, ensuring accurate pass/fail results.
- Fixed the connections in helper CSV and connect function.
- Removed verbose preference from `Test-RestrictCustomScripts`.
- Ensured that the output in `Test-BlockMailForwarding` does not include extra spaces between table headers and data.
- Fixed output in `Test-MailboxAuditingE3` and `Test-MailboxAuditingE5` to correctly align with the new table format.
- Added step 1 and step 2 in `Test-BlockMailForwarding` details to ensure comprehensive compliance checks.
- Fixed the issue with the output in `Test-RestrictCustomScripts` to ensure no extra spaces between table headers and data.
## [0.1.4] - 2024-05-30
### Added
- Test definitions filter function.
- Logging function for future use.
- Test grade written to console.
### Changed
- Updated sync function to include connection info.
- Refactored connect/disconnect functions to evaluate needed connections.
## [0.1.3] - 2024-05-28
### Added
- Array list to store the results of the audit.
- Arraylist tests and helper template.
- New testing function.
- Missing properties to CSV.
### Changed
- Refactored object initialization to source `RecDescription`, `CISControl`, and `CISDescription` properties from the CSV.
- Added `Automated` and `Connection` properties to the output object.
- All test functions aligned with the test-template.
- Initialize-CISAuditResult refactored to use global test definitions.
### Fixed
- Corrected test-template.
- Details added to pass.
### Docs
- Updated comments and documentation for new functions.
## [0.1.2] - 2024-04-29
### Added
- Automated and organized CSV testing and added test 1.1.1.
- Functions to merge tests into an Excel benchmark.
- Public function for merging tests.
- Testing for guest users under test 1.1.4.
- Error handling for `Get-AdminRoleUserLicense`.
- Project URI and icon added to manifest.
### Fixed
- Format for `TestDefinitions.csv`.
- Filename for `Test-AdministrativeAccountCompliance`.
- Error handling in test 1.1.1.
- Properties for skipping and including tests.
### Docs
- Updated comments for new functions.
- Updated help documentation.
- Updated online link in public function.
## [0.1.1] - 2024-04-02
### Fixed
- Fixed Test-ModernAuthExchangeOnline Profile Level in object.
### Added
- CIS Download Notes to Comment-Help Block.
- Notes to README.md for CIS Download.
## [0.1.0-preview0001] - 2024-03-25
### Added
- Initial release of the M365FoundationsCISReport PowerShell module v0.0.1. - Initial release of the M365FoundationsCISReport PowerShell module v0.0.1.
- Function `Invoke-M365SecurityAudit` for conducting a comprehensive security audit in Microsoft 365 environments. - Function `Invoke-M365SecurityAudit` for conducting a comprehensive security audit in Microsoft 365 environments.
- Support for multiple parameter sets including ELevelFilter, IGFilters, RecFilter, and SkipRecFilter to cater to diverse audit requirements. - Support for multiple parameter sets including ELevelFilter, IGFilters, RecFilter, and SkipRecFilter to cater to diverse audit requirements.
@@ -14,4 +171,4 @@ The format is based on and uses the types of changes according to [Keep a Change
- A dynamic test loading system based on CSV input for flexibility in defining audit tests. - A dynamic test loading system based on CSV input for flexibility in defining audit tests.
- Comprehensive verbose logging to detail the steps being performed during an audit. - Comprehensive verbose logging to detail the steps being performed during an audit.
- Comment-help documentation for the `Invoke-M365SecurityAudit` function with examples and usage details. - Comment-help documentation for the `Invoke-M365SecurityAudit` function with examples and usage details.
- Attribution to CIS and licensing information under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License in the README. - Attribution to CIS and licensing information under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License in the README.

93
README copy.md Normal file
View File

@@ -0,0 +1,93 @@
# M365FoundationsCISReport Module
## License
This PowerShell module is based on CIS benchmarks and is distributed under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. This means:
- **Non-commercial**: You may not use the material for commercial purposes.
- **ShareAlike**: If you remix, transform, or build upon the material, you must distribute your contributions under the same license as the original.
- **Attribution**: Appropriate credit must be given, provide a link to the license, and indicate if changes were made.
For full license details, please visit [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License](https://creativecommons.org/licenses/by-nc-sa/4.0/deed.en).
[Register for and download CIS Benchmarks](https://www.cisecurity.org/cis-benchmarks)
## Invoke-M365SecurityAudit
### Synopsis
Invokes a security audit for Microsoft 365 environments.
### Syntax
```powershell
Invoke-M365SecurityAudit -TenantAdminUrl <String> -DomainName <String> [-DoNotConnect] [-DoNotDisconnect] [-NoModuleCheck] [-WhatIf] [-Confirm] [<CommonParameters>]
Invoke-M365SecurityAudit -TenantAdminUrl <String> -DomainName <String> [-ELevel <String>] [-ProfileLevel <String>] [-DoNotConnect] [-DoNotDisconnect] [-NoModuleCheck] [-WhatIf] [-Confirm] [<CommonParameters>]
Invoke-M365SecurityAudit -TenantAdminUrl <String> -DomainName <String> [-IncludeIG1] [-DoNotConnect] [-DoNotDisconnect] [-NoModuleCheck] [-WhatIf] [-Confirm] [<CommonParameters>]
Invoke-M365SecurityAudit -TenantAdminUrl <String> -DomainName <String> [-IncludeIG2] [-DoNotConnect] [-DoNotDisconnect] [-NoModuleCheck] [-WhatIf] [-Confirm] [<CommonParameters>]
Invoke-M365SecurityAudit -TenantAdminUrl <String> -DomainName <String> [-IncludeIG3] [-DoNotConnect] [-DoNotDisconnect] [-NoModuleCheck] [-WhatIf] [-Confirm] [<CommonParameters>]
Invoke-M365SecurityAudit -TenantAdminUrl <String> -DomainName <String> [-IncludeRecommendation <String[]>] [-DoNotConnect] [-DoNotDisconnect] [-NoModuleCheck] [-WhatIf] [-Confirm] [<CommonParameters>]
Invoke-M365SecurityAudit -TenantAdminUrl <String> -DomainName <String> [-SkipRecommendation <String[]>] [-DoNotConnect] [-DoNotDisconnect] [-NoModuleCheck] [-WhatIf] [-Confirm] [<CommonParameters>]
```
### Parameters
| Name | Alias | Description | Required? | Pipeline Input | Default Value |
| - | - | - | - | - | - |
| <nobr>TenantAdminUrl</nobr> | | The URL of the tenant admin. This parameter is mandatory. | true | false | |
| <nobr>DomainName</nobr> | | The domain name of the Microsoft 365 environment. This parameter is mandatory. | true | false | |
| <nobr>ELevel</nobr> | | Specifies the E-Level \(E3 or E5\) for the audit. This parameter is optional and can be combined with the ProfileLevel parameter. | false | false | |
| <nobr>ProfileLevel</nobr> | | Specifies the profile level \(L1 or L2\) for the audit. This parameter is optional and can be combined with the ELevel parameter. | false | false | |
| <nobr>IncludeIG1</nobr> | | If specified, includes tests where IG1 is true. | false | false | False |
| <nobr>IncludeIG2</nobr> | | If specified, includes tests where IG2 is true. | false | false | False |
| <nobr>IncludeIG3</nobr> | | If specified, includes tests where IG3 is true. | false | false | False |
| <nobr>IncludeRecommendation</nobr> | | Specifies specific recommendations to include in the audit. Accepts an array of recommendation numbers. | false | false | |
| <nobr>SkipRecommendation</nobr> | | Specifies specific recommendations to exclude from the audit. Accepts an array of recommendation numbers. | false | false | |
| <nobr>DoNotConnect</nobr> | | If specified, the cmdlet will not establish a connection to Microsoft 365 services. | false | false | False |
| <nobr>DoNotDisconnect</nobr> | | If specified, the cmdlet will not disconnect from Microsoft 365 services after execution. | false | false | False |
| <nobr>NoModuleCheck</nobr> | | If specified, the cmdlet will not check for the presence of required modules. | false | false | False |
| <nobr>WhatIf</nobr> | wi | | false | false | |
| <nobr>Confirm</nobr> | cf | | false | false | |
### Inputs
- None. You cannot pipe objects to Invoke-M365SecurityAudit.
### Outputs
- CISAuditResult\\[\] The cmdlet returns an array of CISAuditResult objects representing the results of the security audit.
### Note
This module is based on CIS benchmarks and is governed by the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. For more details, visit: https://creativecommons.org/licenses/by-nc-sa/4.0/deed.en
### Examples
**EXAMPLE 1**
```powershell
Invoke-M365SecurityAudit -TenantAdminUrl "https://contoso-admin.sharepoint.com" -DomainName "contoso.com" -ELevel "E5" -ProfileLevel "L1"
```
Performs a security audit for the E5 level and L1 profile in the specified Microsoft 365 environment.
**EXAMPLE 2**
```powershell
Invoke-M365SecurityAudit -TenantAdminUrl "https://contoso-admin.sharepoint.com" -DomainName "contoso.com" -IncludeIG1
```
Performs an audit including all tests where IG1 is true.
**EXAMPLE 3**
```powershell
Invoke-M365SecurityAudit -TenantAdminUrl "https://contoso-admin.sharepoint.com" -DomainName "contoso.com" -SkipRecommendation '1.1.3', '2.1.1'
```
Performs an audit while excluding specific recommendations 1.1.3 and 2.1.1.
**EXAMPLE 4**
```powershell
$auditResults = Invoke-M365SecurityAudit -TenantAdminUrl "https://contoso-admin.sharepoint.com" -DomainName "contoso.com"
PS> $auditResults | Export-Csv -Path "auditResults.csv" -NoTypeInformation
```
Captures the audit results into a variable and exports them to a CSV file.
### Links
- [Online Version: [GitHub Repository URL]](#Online Version: [GitHub Repository URL])

BIN
README.md

Binary file not shown.

18
SECURITY.md Normal file
View File

@@ -0,0 +1,18 @@
# Security Policy
## Supported Versions
Use this section to tell people about which versions of your project are
currently being supported with security updates.
| Version | Supported |
| ------- | ------------------ |
| 0.1.4 | :white_check_mark: |
## Reporting a Vulnerability
Use this section to tell people how to report a vulnerability.
Tell them where to go, how often they can expect to get an update on a
reported vulnerability, what to expect if the vulnerability is accepted or
declined, etc.

Binary file not shown.

View File

@@ -0,0 +1,52 @@
# Automation Candidates
## 5.1.1.1 (L1) Ensure Security Defaults is disabled on Azure Active Directory
- `Connect-MgGraph -Scopes "Policy.Read.All"`
- `Get-MgPolicyIdentitySecurityDefaultEnforcementPolicy | ft IsEnabled`
## 5.1.2.1 (L1) Ensure 'Per-user MFA' is disabled
- `Connect-MsolService`
- Commands:
```powershell
$UserList = Get-MsolUser -All | Where-Object { $_.UserType -eq 'Member' }
$Report = @()
foreach ($user in $UserList) {
$PerUserMFAState = $null
if ($user.StrongAuthenticationRequirements) {
$PerUserMFAState = $user.StrongAuthenticationRequirements.State
}
else {
$PerUserMFAState = 'Disabled'
}
$obj = [pscustomobject][ordered]@{
UserPrincipalName = $User.UserPrincipalName
DisplayName = $User.DisplayName
PerUserMFAState = $PerUserMFAState
}
$Report += $obj
}
$Report
```
## 5.1.3.1 (L1) Ensure a dynamic group for guest users is created
- `Connect-MgGraph -Scopes "Group.Read.All"`
- Commands:
```powershell
$groups = Get-MgGroup | Where-Object { $_.GroupTypes -contains "DynamicMembership" }
$groups | ft DisplayName,GroupTypes,MembershipRule
```
## 6.1.4 (L1) Ensure 'AuditBypassEnabled' is not enabled on mailboxes
- `Connect-ExchangeOnline`
- Commands:
```powershell
$MBX = Get-MailboxAuditBypassAssociation -ResultSize unlimited
$MBX | where {$_.AuditBypassEnabled -eq $true} | Format-Table Name,AuditBypassEnabled
```

View File

@@ -1,3 +1,85 @@
Import-Module .\output\module\M365FoundationsCISReport\*\*.psd1 Import-Module .\output\module\M365FoundationsCISReport\*\*.psd1
.\helpers\psDoc-master\src\psDoc.ps1 -moduleName M365FoundationsCISReport -outputDir docs -template ".\helpers\psDoc-master\src\out-html-template.ps1" .\helpers\psDoc-master\src\psDoc.ps1 -moduleName M365FoundationsCISReport -outputDir docs -template ".\helpers\psDoc-master\src\out-html-template.ps1"
.\helpers\psDoc-master\src\psDoc.ps1 -moduleName M365FoundationsCISReport -outputDir ".\" -template ".\helpers\psDoc-master\src\out-markdown-template.ps1" -fileName ".\README.md" .\helpers\psDoc-master\src\psDoc.ps1 -moduleName M365FoundationsCISReport -outputDir ".\" -template ".\helpers\psDoc-master\src\out-markdown-template.ps1" -fileName ".\README.md"
<#
$ver = "v0.1.9"
git checkout main
git pull origin main
git tag -a $ver -m "Release version $ver refactor Update"
git push origin $ver
"Fix: PR #37"
git push origin $ver
# git tag -d $ver
#>
# Refresh authentication to ensure the correct scopes
gh auth refresh -s project,read:project,write:project,repo
# Create the project
gh project create --owner CriticalSolutionsNetwork --title "Test Validation Project"
$repoOwner = "CriticalSolutionsNetwork"
$repoName = "M365FoundationsCISReport"
$directoryPath = ".\source\tests"
$projectName = "Test Validation Project"
# Function to create GitHub issues
function Create-GitHubIssue {
param (
[string]$title,
[string]$body,
[string]$project
)
# Create the issue and add it to the specified project
$issue = gh issue create --repo "$repoOwner/$repoName" --title "$title" --body "$body" --project "$project"
return $issue
}
# Load test definitions from CSV
$testDefinitionsPath = ".\source\helper\TestDefinitions.csv"
$testDefinitions = Import-Csv -Path $testDefinitionsPath
# Iterate over each .ps1 file in the directory
Get-ChildItem -Path $directoryPath -Filter "*.ps1" | ForEach-Object {
$fileName = $_.Name
$testDefinition = $testDefinitions | Where-Object { $_.TestFileName -eq $fileName }
if ($testDefinition) {
$rec = $testDefinition.Rec
$elevel = $testDefinition.ELevel
$profileLevel = $testDefinition.ProfileLevel
$ig1 = $testDefinition.IG1
$ig2 = $testDefinition.IG2
$ig3 = $testDefinition.IG3
$connection = $testDefinition.Connection
$issueTitle = "Rec: $rec - Validate $fileName, ELevel: $elevel, ProfileLevel: $profileLevel, IG1: $ig1, IG2: $ig2, IG3: $ig3, Connection: $connection"
$issueBody = @"
# Validation for $fileName
## Tasks
- [ ] Validate test for a pass
- Description of passing criteria:
- [ ] Validate test for a fail
- Description of failing criteria:
- [ ] Add notes and observations
- Placeholder for additional notes:
"@
# Create the issue using GitHub CLI
try {
Create-GitHubIssue -title "$issueTitle" -body "$issueBody" -project "$projectName"
Write-Output "Created issue for $fileName"
} catch {
Write-Error "Failed to create issue for $fileName : $_"
}
# Introduce a delay of 2 seconds
Start-Sleep -Seconds 2
} else {
Write-Warning "No matching test definition found for $fileName"
}
}

View File

@@ -60,7 +60,7 @@ function Test-MailboxAuditingE3_6.1.2_E3L1_IG1_IG2_IG3 {
} }
if ($missingActions) { if ($missingActions) {
$formattedActions = Format-MissingActions $missingActions $formattedActions = Format-MissingAction $missingActions
$allFailures += "$userUPN`: AuditEnabled - True; $formattedActions" $allFailures += "$userUPN`: AuditEnabled - True; $formattedActions"
} }
# Mark the user as processed # Mark the user as processed
@@ -83,7 +83,7 @@ function Test-MailboxAuditingE3_6.1.2_E3L1_IG1_IG2_IG3 {
} }
} }
function Format-MissingActions { function Format-MissingAction {
param ([array]$missingActions) param ([array]$missingActions)
$actionGroups = @{ $actionGroups = @{

View File

@@ -60,7 +60,7 @@ function Test-MailboxAuditingE5_6.1.3_E5L1_IG1_IG2_IG3 {
} }
if ($missingActions) { if ($missingActions) {
$formattedActions = Format-MissingActions $missingActions $formattedActions = Format-MissingAction $missingActions
$allFailures += "$userUPN`: AuditEnabled - True; $formattedActions" $allFailures += "$userUPN`: AuditEnabled - True; $formattedActions"
} }
else { else {
@@ -92,7 +92,7 @@ function Test-MailboxAuditingE5_6.1.3_E5L1_IG1_IG2_IG3 {
} }
} }
function Format-MissingActions { function Format-MissingAction {
param ([array]$missingActions) param ([array]$missingActions)
$actionGroups = @{ $actionGroups = @{

View File

@@ -0,0 +1,97 @@
function Test-Template {
[CmdletBinding()]
param (
# Parameters can be added if needed
)
begin {
# Initialization code, if needed
# Load necessary scripts, define variables, etc.
}
process {
# Fetch relevant data
# Example: $data = Get-SomeData
# Process the data to evaluate compliance
# Example: $compliantItems = $data | Where-Object { $_.Property -eq 'ExpectedValue' }
# Example: $nonCompliantItems = $data | Where-Object { $_.Property -ne 'ExpectedValue' }
# Prepare failure reasons for non-compliant items
$failureReasons = $nonCompliantItems | ForEach-Object {
# Example: "Item: $($_.Name) - Reason: Missing expected value"
}
$failureReasons = $failureReasons -join "`n"
# Prepare details for non-compliant items
$nonCompliantDetails = $nonCompliantItems | ForEach-Object {
# Example: "$($_.Name) - Value: $($_.Property)"
}
$nonCompliantDetails = $nonCompliantDetails -join "`n"
# Prepare details based on compliance
$details = if ($nonCompliantItems) {
"Non-Compliant Items: $($nonCompliantItems.Count)`nDetails:`n$nonCompliantDetails"
} else {
"Compliant Items: $($compliantItems.Count)"
}
# Create and populate the CISAuditResult object
$auditResult = [CISAuditResult]::new()
$auditResult.Status = if ($nonCompliantItems) { 'Fail' } else { 'Pass' }
$auditResult.ELevel = 'E3' # Modify as needed
$auditResult.ProfileLevel = 'L1' # Modify as needed
$auditResult.Rec = '1.1.1' # Modify as needed
$auditResult.RecDescription = "Description of the recommendation" # Modify as needed
$auditResult.CISControlVer = 'v8' # Modify as needed
$auditResult.CISControl = "5.4" # Modify as needed
$auditResult.CISDescription = "Description of the CIS control" # Modify as needed
$auditResult.IG1 = $true # Modify as needed
$auditResult.IG2 = $true # Modify as needed
$auditResult.IG3 = $true # Modify as needed
$auditResult.Result = $nonCompliantItems.Count -eq 0
$auditResult.Details = $details
$auditResult.FailureReason = if ($nonCompliantItems) {
"Non-compliant items:`n$failureReasons"
} else {
"N/A"
}
# Example output object for a pass result
# Status : Pass
# ELevel : E3
# ProfileLevel : L2
# Rec : 8.1.1
# RecDescription : Ensure external file sharing in Teams is enabled for only approved cloud storage services
# CISControlVer : v8
# CISControl : 3.3
# CISDescription : Configure Data Access Control Lists
# IG1 : True
# IG2 : True
# IG3 : True
# Result : True
# Details : Compliant Items: 5
# FailureReason : N/A
# Example output object for a fail result
# Status : Fail
# ELevel : E3
# ProfileLevel : L2
# Rec : 8.1.1
# RecDescription : Ensure external file sharing in Teams is enabled for only approved cloud storage services
# CISControlVer : v8
# CISControl : 3.3
# CISDescription : Configure Data Access Control Lists
# IG1 : True
# IG2 : True
# IG3 : True
# Result : False
# Details : Non-Compliant Items: 2
# FailureReason : Non-compliant items:`nUsername | Roles | HybridStatus | Missing Licence
}
end {
# Return the audit result
return $auditResult
}
}

View File

@@ -0,0 +1,45 @@
function Get-AdminRoleUserLicense {
[CmdletBinding()]
param (
[Parameter(Mandatory = $false)]
[bool]$SkipGraphConnection = $false
)
# Connect to Microsoft Graph if not skipping connection
if (-not $SkipGraphConnection) {
Connect-MgGraph -Scopes "Directory.Read.All", "Domain.Read.All", "Policy.Read.All", "Organization.Read.All" -NoWelcome
}
$adminRoleUsers = @()
$userIds = @()
$adminroles = Get-MgRoleManagementDirectoryRoleDefinition | Where-Object { $_.DisplayName -like "*Admin*" }
foreach ($role in $adminroles) {
$usersInRole = Get-MgRoleManagementDirectoryRoleAssignment -Filter "roleDefinitionId eq '$($role.Id)'"
foreach ($user in $usersInRole) {
$userIds += $user.PrincipalId
$userDetails = Get-MgUser -UserId $user.PrincipalId -Property "DisplayName, UserPrincipalName, Id, onPremisesSyncEnabled"
$adminRoleUsers += [PSCustomObject]@{
RoleName = $role.DisplayName
UserName = $userDetails.DisplayName
UserPrincipalName = $userDetails.UserPrincipalName
UserId = $userDetails.Id
HybridUser = $userDetails.onPremisesSyncEnabled
Licenses = "" # Placeholder for licenses, to be filled later
}
}
}
foreach ($userId in $userIds | Select-Object -Unique) {
$licenses = Get-MgUserLicenseDetail -UserId $userId
$licenseList = ($licenses.SkuPartNumber -join '|')
$adminRoleUsers | Where-Object { $_.UserId -eq $userId } | ForEach-Object {
$_.Licenses = $licenseList
}
}
return $adminRoleUsers
}

View File

@@ -2,6 +2,8 @@ class CISAuditResult {
[string]$Status [string]$Status
[string]$ELevel [string]$ELevel
[string]$ProfileLevel [string]$ProfileLevel
[bool]$Automated
[string]$Connection
[string]$Rec [string]$Rec
[string]$RecDescription [string]$RecDescription
[string]$CISControlVer = 'v8' [string]$CISControlVer = 'v8'

View File

@@ -102,10 +102,10 @@ PrivateData = @{
LicenseUri = 'https://creativecommons.org/licenses/by-nc-sa/4.0/deed.en' LicenseUri = 'https://creativecommons.org/licenses/by-nc-sa/4.0/deed.en'
# A URL to the main website for this project. # A URL to the main website for this project.
# ProjectUri = '' ProjectUri = 'https://github.com/CriticalSolutionsNetwork/M365FoundationsCISReport'
# A URL to an icon representing this module. # A URL to an icon representing this module.
# IconUri = '' IconUri = 'https://csn-source.s3.us-east-2.amazonaws.com/CSN-Icon.png'
# ReleaseNotes of this module # ReleaseNotes of this module
ReleaseNotes = '' ReleaseNotes = ''

View File

@@ -1,33 +1,37 @@
function Assert-ModuleAvailability { function Assert-ModuleAvailability {
[OutputType([void]) ]
param( param(
[string]$ModuleName, [string]$ModuleName,
[string]$RequiredVersion, [string]$RequiredVersion,
[string]$SubModuleName [string[]]$SubModules = @()
) )
try { try {
$module = Get-Module -ListAvailable -Name $ModuleName | Where-Object { $_.Version -ge [version]$RequiredVersion } $module = Get-Module -ListAvailable -Name $ModuleName | Where-Object { $_.Version -ge [version]$RequiredVersion }
if ($null -eq $module) {$auditResult.Profile if ($null -eq $module) {
Write-Host "Installing $ModuleName module..." Write-Information "Installing $ModuleName module..." -InformationAction Continue
Install-Module -Name $ModuleName -RequiredVersion $RequiredVersion -Force -AllowClobber -Scope CurrentUser | Out-Null Install-Module -Name $ModuleName -RequiredVersion $RequiredVersion -Force -AllowClobber -Scope CurrentUser | Out-Null
} }
elseif ($module.Version -lt [version]$RequiredVersion) { elseif ($module.Version -lt [version]$RequiredVersion) {
Write-Host "Updating $ModuleName module to required version..." Write-Information "Updating $ModuleName module to required version..." -InformationAction Continue
Update-Module -Name $ModuleName -RequiredVersion $RequiredVersion -Force | Out-Null Update-Module -Name $ModuleName -RequiredVersion $RequiredVersion -Force | Out-Null
} }
else { else {
Write-Host "$ModuleName module is already at required version or newer." Write-Information "$ModuleName module is already at required version or newer." -InformationAction Continue
} }
if ($SubModuleName) { if ($SubModules.Count -gt 0) {
Import-Module -Name "$ModuleName.$SubModuleName" -RequiredVersion $RequiredVersion -ErrorAction Stop | Out-Null foreach ($subModule in $SubModules) {
} Write-Information "Importing submodule $ModuleName.$subModule..." -InformationAction Continue
else { Import-Module -Name "$ModuleName.$subModule" -RequiredVersion $RequiredVersion -ErrorAction Stop | Out-Null
}
} else {
Write-Information "Importing module $ModuleName..." -InformationAction Continue
Import-Module -Name $ModuleName -RequiredVersion $RequiredVersion -ErrorAction Stop | Out-Null Import-Module -Name $ModuleName -RequiredVersion $RequiredVersion -ErrorAction Stop | Out-Null
} }
} }
catch { catch {
Write-Warning "An error occurred with module $ModuleName`: $_" Write-Warning "An error occurred with module $ModuleName`: $_"
} }
} }

View File

@@ -1,56 +1,59 @@
function Connect-M365Suite { function Connect-M365Suite {
[OutputType([void])]
[CmdletBinding()] [CmdletBinding()]
param ( param (
# Parameter to specify the SharePoint Online Tenant Admin URL [Parameter(Mandatory=$false)]
[string]$TenantAdminUrl,
[Parameter(Mandatory)] [Parameter(Mandatory)]
[string]$TenantAdminUrl [string[]]$RequiredConnections
) )
$VerbosePreference = "SilentlyContinue"
$VerbosePreference = "SilentlyContinue"
try { try {
if ($RequiredConnections -contains "AzureAD" -or $RequiredConnections -contains "AzureAD | EXO" -or $RequiredConnections -contains "AzureAD | EXO | Microsoft Graph") {
Write-Host "Connecting to Azure Active Directory..." -ForegroundColor Cyan
Connect-AzureAD | Out-Null
Write-Host "Successfully connected to Azure Active Directory." -ForegroundColor Green
}
# Attempt to connect to Azure Active Directory if ($RequiredConnections -contains "Microsoft Graph" -or $RequiredConnections -contains "AzureAD | EXO | Microsoft Graph") {
Write-Host "Connecting to Azure Active Directory..." -ForegroundColor Cyan
Connect-AzureAD | Out-Null
Write-Host "Successfully connected to Azure Active Directory." -ForegroundColor Green
# Attempt to connect to Exchange Online
Write-Host "Connecting to Exchange Online..." -ForegroundColor Cyan
Connect-ExchangeOnline | Out-Null
Write-Host "Successfully connected to Exchange Online." -ForegroundColor Green
try {
# Attempt to connect to Microsoft Graph with specified scopes
Write-Host "Connecting to Microsoft Graph with scopes: Directory.Read.All, Domain.Read.All, Policy.Read.All, Organization.Read.All" -ForegroundColor Cyan Write-Host "Connecting to Microsoft Graph with scopes: Directory.Read.All, Domain.Read.All, Policy.Read.All, Organization.Read.All" -ForegroundColor Cyan
Connect-MgGraph -Scopes "Directory.Read.All", "Domain.Read.All", "Policy.Read.All", "Organization.Read.All" -NoWelcome | Out-Null try {
Write-Host "Successfully connected to Microsoft Graph with specified scopes." -ForegroundColor Green Connect-MgGraph -Scopes "Directory.Read.All", "Domain.Read.All", "Policy.Read.All", "Organization.Read.All" -NoWelcome | Out-Null
} Write-Host "Successfully connected to Microsoft Graph with specified scopes." -ForegroundColor Green
catch { }
Write-Host "Failed to connect o MgGraph, attempting device auth." -ForegroundColor Yellow catch {
# Attempt to connect to Microsoft Graph with specified scopes Write-Host "Failed to connect to MgGraph, attempting device auth." -ForegroundColor Yellow
Write-Host "Connecting to Microsoft Graph using device auth with scopes: Directory.Read.All, Domain.Read.All, Policy.Read.All, Organization.Read.All" -ForegroundColor Cyan Connect-MgGraph -Scopes "Directory.Read.All", "Domain.Read.All", "Policy.Read.All", "Organization.Read.All" -UseDeviceCode -NoWelcome | Out-Null
Connect-MgGraph -Scopes "Directory.Read.All", "Domain.Read.All", "Policy.Read.All", "Organization.Read.All" -UseDeviceCode -NoWelcome | Out-Null Write-Host "Successfully connected to Microsoft Graph with specified scopes." -ForegroundColor Green
Write-Host "Successfully connected to Microsoft Graph with specified scopes." -ForegroundColor Green }
} }
# Validate SharePoint Online Tenant Admin URL if ($RequiredConnections -contains "EXO" -or $RequiredConnections -contains "AzureAD | EXO" -or $RequiredConnections -contains "Microsoft Teams | EXO" -or $RequiredConnections -contains "AzureAD | EXO | Microsoft Graph") {
if (-not $TenantAdminUrl) { Write-Host "Connecting to Exchange Online..." -ForegroundColor Cyan
throw "SharePoint Online Tenant Admin URL is required." Connect-ExchangeOnline | Out-Null
Write-Host "Successfully connected to Exchange Online." -ForegroundColor Green
} }
# Attempt to connect to SharePoint Online if ($RequiredConnections -contains "SPO") {
Write-Host "Connecting to SharePoint Online..." -ForegroundColor Cyan Write-Host "Connecting to SharePoint Online..." -ForegroundColor Cyan
Connect-SPOService -Url $TenantAdminUrl | Out-Null Connect-SPOService -Url $TenantAdminUrl | Out-Null
Write-Host "Successfully connected to SharePoint Online." -ForegroundColor Green Write-Host "Successfully connected to SharePoint Online." -ForegroundColor Green
}
# Attempt to connect to Microsoft Teams if ($RequiredConnections -contains "Microsoft Teams" -or $RequiredConnections -contains "Microsoft Teams | EXO") {
Write-Host "Connecting to Microsoft Teams..." -ForegroundColor Cyan Write-Host "Connecting to Microsoft Teams..." -ForegroundColor Cyan
Connect-MicrosoftTeams | Out-Null Connect-MicrosoftTeams | Out-Null
Write-Host "Successfully connected to Microsoft Teams." -ForegroundColor Green Write-Host "Successfully connected to Microsoft Teams." -ForegroundColor Green
}
} }
catch { catch {
$VerbosePreference = "Continue" $VerbosePreference = "Continue"
Write-Host "There was an error establishing one or more connections: $_" -ForegroundColor Red Write-Host "There was an error establishing one or more connections: $_" -ForegroundColor Red
throw $_ throw $_
} }
$VerbosePreference = "Continue" $VerbosePreference = "Continue"
} }

View File

@@ -1,39 +1,60 @@
function Disconnect-M365Suite { function Disconnect-M365Suite {
[OutputType([void])]
param (
[Parameter(Mandatory)]
[string[]]$RequiredConnections
)
# Clean up sessions # Clean up sessions
try { try {
Write-Host "Disconnecting from Exchange Online..." -ForegroundColor Green if ($RequiredConnections -contains "EXO" -or $RequiredConnections -contains "AzureAD | EXO" -or $RequiredConnections -contains "Microsoft Teams | EXO") {
Disconnect-ExchangeOnline -Confirm:$false | Out-Null Write-Host "Disconnecting from Exchange Online..." -ForegroundColor Green
Disconnect-ExchangeOnline -Confirm:$false | Out-Null
}
} }
catch { catch {
Write-Warning "Failed to disconnect from Exchange Online: $_" Write-Warning "Failed to disconnect from Exchange Online: $_"
} }
try { try {
Write-Host "Disconnecting from Azure AD..." -ForegroundColor Green if ($RequiredConnections -contains "AzureAD" -or $RequiredConnections -contains "AzureAD | EXO") {
Disconnect-AzureAD | Out-Null Write-Host "Disconnecting from Azure AD..." -ForegroundColor Green
Disconnect-AzureAD | Out-Null
}
} }
catch { catch {
Write-Warning "Failed to disconnect from Azure AD: $_" Write-Warning "Failed to disconnect from Azure AD: $_"
} }
try { try {
Write-Host "Disconnecting from Microsoft Graph..." -ForegroundColor Green if ($RequiredConnections -contains "Microsoft Graph") {
Disconnect-MgGraph | Out-Null Write-Host "Disconnecting from Microsoft Graph..." -ForegroundColor Green
Disconnect-MgGraph | Out-Null
}
} }
catch { catch {
Write-Warning "Failed to disconnect from Microsoft Graph: $_" Write-Warning "Failed to disconnect from Microsoft Graph: $_"
} }
try { try {
Write-Host "Disconnecting from SharePoint Online..." -ForegroundColor Green if ($RequiredConnections -contains "SPO") {
Disconnect-SPOService | Out-Null Write-Host "Disconnecting from SharePoint Online..." -ForegroundColor Green
Disconnect-SPOService | Out-Null
}
} }
catch { catch {
Write-Warning "Failed to disconnect from SharePoint Online: $_" Write-Warning "Failed to disconnect from SharePoint Online: $_"
} }
try { try {
Write-Host "Disconnecting from Microsoft Teams..." -ForegroundColor Green if ($RequiredConnections -contains "Microsoft Teams" -or $RequiredConnections -contains "Microsoft Teams | EXO") {
Disconnect-MicrosoftTeams | Out-Null Write-Host "Disconnecting from Microsoft Teams..." -ForegroundColor Green
Disconnect-MicrosoftTeams | Out-Null
}
} }
catch { catch {
Write-Warning "Failed to disconnect from Microsoft Teams: $_" Write-Warning "Failed to disconnect from Microsoft Teams: $_"
} }
Write-Host "All sessions have been disconnected." -ForegroundColor Green
Write-Host "All necessary sessions have been disconnected." -ForegroundColor Green
} }

View File

@@ -0,0 +1,29 @@
function Format-MissingAction {
[CmdletBinding()]
[OutputType([hashtable])]
param (
[array]$missingActions
)
$actionGroups = @{
"Admin" = @()
"Delegate" = @()
"Owner" = @()
}
foreach ($action in $missingActions) {
if ($action -match "(Admin|Delegate|Owner) action '([^']+)' missing") {
$type = $matches[1]
$actionName = $matches[2]
$actionGroups[$type] += $actionName
}
}
$formattedResults = @{
Admin = $actionGroups["Admin"] -join ', '
Delegate = $actionGroups["Delegate"] -join ', '
Owner = $actionGroups["Owner"] -join ', '
}
return $formattedResults
}

View File

@@ -0,0 +1,19 @@
function Format-RequiredModuleList {
[CmdletBinding()]
[OutputType([string])]
param (
[Parameter(Mandatory = $true)]
[System.Object[]]$RequiredModules
)
$requiredModulesFormatted = ""
foreach ($module in $RequiredModules) {
if ($module.SubModules -and $module.SubModules.Count -gt 0) {
$subModulesFormatted = $module.SubModules -join ', '
$requiredModulesFormatted += "$($module.ModuleName) (SubModules: $subModulesFormatted), "
} else {
$requiredModulesFormatted += "$($module.ModuleName), "
}
}
return $requiredModulesFormatted.TrimEnd(", ")
}

View File

@@ -0,0 +1,24 @@
function Get-MostCommonWord {
[CmdletBinding()]
[OutputType([string])]
param (
[Parameter(Mandatory = $true)]
[string[]]$InputStrings
)
# Combine all strings into one large string
$allText = $InputStrings -join ' '
# Split the large string into words
$words = $allText -split '\s+'
# Group words and count occurrences
$wordGroups = $words | Group-Object | Sort-Object Count -Descending
# Return the most common word if it occurs at least 3 times
if ($wordGroups.Count -gt 0 -and $wordGroups[0].Count -ge 3) {
return $wordGroups[0].Name
} else {
return $null
}
}

View File

@@ -0,0 +1,31 @@
function Get-RequiredModule {
[CmdletBinding(DefaultParameterSetName = 'AuditFunction')]
[OutputType([System.Object[]])]
param (
[Parameter(Mandatory = $true, ParameterSetName = 'AuditFunction')]
[switch]$AuditFunction,
[Parameter(Mandatory = $true, ParameterSetName = 'SyncFunction')]
[switch]$SyncFunction
)
switch ($PSCmdlet.ParameterSetName) {
'AuditFunction' {
return @(
@{ ModuleName = "ExchangeOnlineManagement"; RequiredVersion = "3.3.0"; SubModules = @() },
@{ ModuleName = "AzureAD"; RequiredVersion = "2.0.2.182"; SubModules = @() },
@{ ModuleName = "Microsoft.Graph"; RequiredVersion = "2.4.0"; SubModules = @("Groups", "DeviceManagement", "Users", "Identity.DirectoryManagement", "Identity.SignIns") },
@{ ModuleName = "Microsoft.Online.SharePoint.PowerShell"; RequiredVersion = "16.0.24009.12000"; SubModules = @() },
@{ ModuleName = "MicrosoftTeams"; RequiredVersion = "5.5.0"; SubModules = @() }
)
}
'SyncFunction' {
return @(
@{ ModuleName = "ImportExcel"; RequiredVersion = "7.8.9"; SubModules = @() }
)
}
default {
throw "Please specify either -AuditFunction or -SyncFunction switch."
}
}
}

View File

@@ -0,0 +1,65 @@
function Get-TestDefinitionsObject {
[CmdletBinding()]
[OutputType([object[]])]
param (
[Parameter(Mandatory = $true)]
[object[]]$TestDefinitions,
[Parameter(Mandatory = $true)]
[string]$ParameterSetName,
[string]$ELevel,
[string]$ProfileLevel,
[string[]]$IncludeRecommendation,
[string[]]$SkipRecommendation
)
Write-Verbose "Initial test definitions count: $($TestDefinitions.Count)"
switch ($ParameterSetName) {
'ELevelFilter' {
Write-Verbose "Applying ELevelFilter"
if ($null -ne $ELevel -and $null -ne $ProfileLevel) {
Write-Verbose "Filtering on ELevel = $ELevel and ProfileLevel = $ProfileLevel"
$TestDefinitions = $TestDefinitions | Where-Object {
$_.ELevel -eq $ELevel -and $_.ProfileLevel -eq $ProfileLevel
}
}
elseif ($null -ne $ELevel) {
Write-Verbose "Filtering on ELevel = $ELevel"
$TestDefinitions = $TestDefinitions | Where-Object {
$_.ELevel -eq $ELevel
}
}
elseif ($null -ne $ProfileLevel) {
Write-Verbose "Filtering on ProfileLevel = $ProfileLevel"
$TestDefinitions = $TestDefinitions | Where-Object {
$_.ProfileLevel -eq $ProfileLevel
}
}
}
'IG1Filter' {
Write-Verbose "Applying IG1Filter"
$TestDefinitions = $TestDefinitions | Where-Object { $_.IG1 -eq 'TRUE' }
}
'IG2Filter' {
Write-Verbose "Applying IG2Filter"
$TestDefinitions = $TestDefinitions | Where-Object { $_.IG2 -eq 'TRUE' }
}
'IG3Filter' {
Write-Verbose "Applying IG3Filter"
$TestDefinitions = $TestDefinitions | Where-Object { $_.IG3 -eq 'TRUE' }
}
'RecFilter' {
Write-Verbose "Applying RecFilter"
$TestDefinitions = $TestDefinitions | Where-Object { $IncludeRecommendation -contains $_.Rec }
}
'SkipRecFilter' {
Write-Verbose "Applying SkipRecFilter"
$TestDefinitions = $TestDefinitions | Where-Object { $SkipRecommendation -notcontains $_.Rec }
}
}
Write-Verbose "Filtered test definitions count: $($TestDefinitions.Count)"
return $TestDefinitions
}

View File

@@ -0,0 +1,28 @@
function Get-UniqueConnection {
[CmdletBinding()]
[OutputType([string[]])]
param (
[Parameter(Mandatory = $true)]
[string[]]$Connections
)
$uniqueConnections = @()
if ($Connections -contains "AzureAD" -or $Connections -contains "AzureAD | EXO" -or $Connections -contains "AzureAD | EXO | Microsoft Graph") {
$uniqueConnections += "AzureAD"
}
if ($Connections -contains "Microsoft Graph" -or $Connections -contains "AzureAD | EXO | Microsoft Graph") {
$uniqueConnections += "Microsoft Graph"
}
if ($Connections -contains "EXO" -or $Connections -contains "AzureAD | EXO" -or $Connections -contains "Microsoft Teams | EXO" -or $Connections -contains "AzureAD | EXO | Microsoft Graph") {
$uniqueConnections += "EXO"
}
if ($Connections -contains "SPO") {
$uniqueConnections += "SPO"
}
if ($Connections -contains "Microsoft Teams" -or $Connections -contains "Microsoft Teams | EXO") {
$uniqueConnections += "Microsoft Teams"
}
return $uniqueConnections | Sort-Object -Unique
}

View File

@@ -0,0 +1,62 @@
function Initialize-CISAuditResult {
[CmdletBinding()]
[OutputType([CISAuditResult])]
param (
[Parameter(Mandatory = $true)]
[string]$Rec,
[Parameter(Mandatory = $true, ParameterSetName = 'Full')]
[bool]$Result,
[Parameter(Mandatory = $true, ParameterSetName = 'Full')]
[string]$Status,
[Parameter(Mandatory = $true, ParameterSetName = 'Full')]
[string]$Details,
[Parameter(Mandatory = $true, ParameterSetName = 'Full')]
[string]$FailureReason,
[Parameter(ParameterSetName = 'Error')]
[switch]$Failure
)
# Import the test definitions CSV file
$testDefinitions = $script:TestDefinitionsObject
# Find the row that matches the provided recommendation (Rec)
$testDefinition = $testDefinitions | Where-Object { $_.Rec -eq $Rec }
if (-not $testDefinition) {
throw "Test definition for recommendation '$Rec' not found."
}
# Create an instance of CISAuditResult and populate it
$auditResult = [CISAuditResult]::new()
$auditResult.Rec = $Rec
$auditResult.ELevel = $testDefinition.ELevel
$auditResult.ProfileLevel = $testDefinition.ProfileLevel
$auditResult.IG1 = [bool]::Parse($testDefinition.IG1)
$auditResult.IG2 = [bool]::Parse($testDefinition.IG2)
$auditResult.IG3 = [bool]::Parse($testDefinition.IG3)
$auditResult.RecDescription = $testDefinition.RecDescription
$auditResult.CISControl = $testDefinition.CISControl
$auditResult.CISDescription = $testDefinition.CISDescription
$auditResult.Automated = [bool]::Parse($testDefinition.Automated)
$auditResult.Connection = $testDefinition.Connection
$auditResult.CISControlVer = 'v8'
if ($PSCmdlet.ParameterSetName -eq 'Full') {
$auditResult.Result = $Result
$auditResult.Status = $Status
$auditResult.Details = $Details
$auditResult.FailureReason = $FailureReason
} elseif ($PSCmdlet.ParameterSetName -eq 'Error') {
$auditResult.Result = $false
$auditResult.Status = 'Fail'
$auditResult.Details = "An error occurred while processing the test."
$auditResult.FailureReason = "Initialization error: Failed to process the test."
}
return $auditResult
}

View File

@@ -0,0 +1,35 @@
function Invoke-TestFunction {
[OutputType([CISAuditResult[]])]
param (
[Parameter(Mandatory = $true)]
[PSObject]$FunctionFile,
[Parameter(Mandatory = $false)]
[string]$DomainName
)
$functionName = $FunctionFile.BaseName
$functionCmd = Get-Command -Name $functionName
# Check if the test function needs DomainName parameter
$paramList = @{}
if ('DomainName' -in $functionCmd.Parameters.Keys) {
$paramList.DomainName = $DomainName
}
# Use splatting to pass parameters
Write-Verbose "Running $functionName..."
try {
$result = & $functionName @paramList
# Assuming each function returns an array of CISAuditResult or a single CISAuditResult
return $result
}
catch {
Write-Error "An error occurred during the test: $_"
$script:FailedTests.Add([PSCustomObject]@{ Test = $functionName; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $functionName -Failure
return $auditResult
}
}

View File

@@ -0,0 +1,32 @@
function Measure-AuditResult {
[OutputType([void])]
param (
[Parameter(Mandatory = $true)]
[System.Collections.ArrayList]$AllAuditResults,
[Parameter(Mandatory = $false)]
[System.Collections.ArrayList]$FailedTests
)
# Calculate the total number of tests
$totalTests = $AllAuditResults.Count
# Calculate the number of passed tests
$passedTests = $AllAuditResults.ToArray() | Where-Object { $_.Result -eq $true } | Measure-Object | Select-Object -ExpandProperty Count
# Calculate the pass percentage
$passPercentage = if ($totalTests -eq 0) { 0 } else { [math]::Round(($passedTests / $totalTests) * 100, 2) }
# Display the pass percentage to the user
Write-Host "Audit completed. $passedTests out of $totalTests tests passed." -ForegroundColor Cyan
Write-Host "Your passing percentage is $passPercentage%."
# Display details of failed tests
if ($FailedTests.Count -gt 0) {
Write-Host "The following tests failed to complete:" -ForegroundColor Red
foreach ($failedTest in $FailedTests) {
Write-Host "Test: $($failedTest.Test)" -ForegroundColor Yellow
Write-Host "Error: $($failedTest.Error)" -ForegroundColor Yellow
}
}
}

View File

@@ -0,0 +1,42 @@
function Merge-CISExcelAndCsvData {
[CmdletBinding(DefaultParameterSetName = 'CsvInput')]
[OutputType([PSCustomObject[]])]
param (
[Parameter(Mandatory = $true)]
[string]$ExcelPath,
[Parameter(Mandatory = $true)]
[string]$WorksheetName,
[Parameter(Mandatory = $true, ParameterSetName = 'CsvInput')]
[string]$CsvPath,
[Parameter(Mandatory = $true, ParameterSetName = 'ObjectInput')]
[CISAuditResult[]]$AuditResults
)
process {
# Import data from Excel
$import = Import-Excel -Path $ExcelPath -WorksheetName $WorksheetName
# Import data from CSV or use provided object
$csvData = if ($PSCmdlet.ParameterSetName -eq 'CsvInput') {
Import-Csv -Path $CsvPath
} else {
$AuditResults
}
# Iterate over each item in the imported Excel object and merge with CSV data or audit results
$mergedData = foreach ($item in $import) {
$csvRow = $csvData | Where-Object { $_.Rec -eq $item.'recommendation #' }
if ($csvRow) {
New-MergedObject -ExcelItem $item -CsvRow $csvRow
} else {
New-MergedObject -ExcelItem $item -CsvRow ([PSCustomObject]@{Connection=$null;Status=$null; Details=$null; FailureReason=$null })
}
}
# Return the merged data
return $mergedData
}
}

View File

@@ -0,0 +1,22 @@
function New-MergedObject {
[CmdletBinding()]
[OutputType([PSCustomObject])]
param (
[Parameter(Mandatory = $true)]
[psobject]$ExcelItem,
[Parameter(Mandatory = $true)]
[psobject]$CsvRow
)
$newObject = New-Object PSObject
foreach ($property in $ExcelItem.PSObject.Properties) {
$newObject | Add-Member -MemberType NoteProperty -Name $property.Name -Value $property.Value
}
$newObject | Add-Member -MemberType NoteProperty -Name 'CSV_Connection' -Value $CsvRow.Connection
$newObject | Add-Member -MemberType NoteProperty -Name 'CSV_Status' -Value $CsvRow.Status
$newObject | Add-Member -MemberType NoteProperty -Name 'CSV_Details' -Value $CsvRow.Details
$newObject | Add-Member -MemberType NoteProperty -Name 'CSV_FailureReason' -Value $CsvRow.FailureReason
return $newObject
}

View File

@@ -0,0 +1,22 @@
function Test-IsAdmin {
<#
.SYNOPSIS
Checks if the current user is an administrator on the machine.
.DESCRIPTION
This private function returns a Boolean value indicating whether
the current user has administrator privileges on the machine.
It does this by creating a new WindowsPrincipal object, passing
in a WindowsIdentity object representing the current user, and
then checking if that principal is in the Administrator role.
.INPUTS
None.
.OUTPUTS
Boolean. Returns True if the current user is an administrator, and False otherwise.
.EXAMPLE
PS C:\> Test-IsAdmin
True
#>
# Create a new WindowsPrincipal object for the current user and check if it is in the Administrator role
(New-Object Security.Principal.WindowsPrincipal ([Security.Principal.WindowsIdentity]::GetCurrent())).IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator)
}

View File

@@ -0,0 +1,34 @@
function Update-CISExcelWorksheet {
[OutputType([void])]
[CmdletBinding()]
param (
[Parameter(Mandatory = $true)]
[string]$ExcelPath,
[Parameter(Mandatory = $true)]
[string]$WorksheetName,
[Parameter(Mandatory = $true)]
[psobject[]]$Data,
[Parameter(Mandatory = $false)]
[int]$StartingRowIndex = 2 # Default starting row index, assuming row 1 has headers
)
process {
# Load the existing Excel sheet
$excelPackage = Open-ExcelPackage -Path $ExcelPath
$worksheet = $excelPackage.Workbook.Worksheets[$WorksheetName]
if (-not $worksheet) {
throw "Worksheet '$WorksheetName' not found in '$ExcelPath'"
}
# Update the worksheet with the provided data
Update-WorksheetCell -Worksheet $worksheet -Data $Data -StartingRowIndex $StartingRowIndex
# Save and close the Excel package
Close-ExcelPackage $excelPackage
}
}

View File

@@ -0,0 +1,29 @@
function Update-WorksheetCell {
[OutputType([void])]
param (
$Worksheet,
$Data,
$StartingRowIndex
)
# Check and set headers
$firstItem = $Data[0]
$colIndex = 1
foreach ($property in $firstItem.PSObject.Properties) {
if ($StartingRowIndex -eq 2 -and $Worksheet.Cells[1, $colIndex].Value -eq $null) {
$Worksheet.Cells[1, $colIndex].Value = $property.Name
}
$colIndex++
}
# Iterate over each row in the data and update cells
$rowIndex = $StartingRowIndex
foreach ($item in $Data) {
$colIndex = 1
foreach ($property in $item.PSObject.Properties) {
$Worksheet.Cells[$rowIndex, $colIndex].Value = $property.Value
$colIndex++
}
$rowIndex++
}
}

View File

@@ -0,0 +1,212 @@
function Write-AuditLog {
<#
.SYNOPSIS
Writes log messages to the console and updates the script-wide log variable.
.DESCRIPTION
The Write-AuditLog function writes log messages to the console based on the severity (Verbose, Warning, or Error) and updates
the script-wide log variable ($script:LogString) with the log entry. You can use the Start, End, and EndFunction switches to
manage the lifecycle of the logging.
.INPUTS
System.String
You can pipe a string to the Write-AuditLog function as the Message parameter.
You can also pipe an object with a Severity property as the Severity parameter.
.OUTPUTS
None
The Write-AuditLog function doesn't output any objects to the pipeline. It writes messages to the console and updates the
script-wide log variable ($script:LogString).
.PARAMETER BeginFunction
Sets the message to "Begin [FunctionName] function log.", where FunctionName is the name of the calling function, and adds it to the log variable.
.PARAMETER Message
The message string to log.
.PARAMETER Severity
The severity of the log message. Accepted values are 'Information', 'Warning', and 'Error'. Defaults to 'Information'.
.PARAMETER Start
Initializes the script-wide log variable and sets the message to "Begin [FunctionName] Log.", where FunctionName is the name of the calling function.
.PARAMETER End
Sets the message to "End Log" and exports the log to a CSV file if the OutputPath parameter is provided.
.PARAMETER EndFunction
Sets the message to "End [FunctionName] log.", where FunctionName is the name of the calling function, and adds it to the log variable.
.PARAMETER OutputPath
The file path for exporting the log to a CSV file when using the End switch.
.EXAMPLE
Write-AuditLog -Message "This is a test message."
Writes a test message with the default severity (Information) to the console and adds it to the log variable.
.EXAMPLE
Write-AuditLog -Message "This is a warning message." -Severity "Warning"
Writes a warning message to the console and adds it to the log variable.
.EXAMPLE
Write-AuditLog -Start
Initializes the log variable and sets the message to "Begin [FunctionName] Log.", where FunctionName is the name of the calling function.
.EXAMPLE
Write-AuditLog -BeginFunction
Sets the message to "Begin [FunctionName] function log.", where FunctionName is the name of the calling function, and adds it to the log variable.
.EXAMPLE
Write-AuditLog -EndFunction
Sets the message to "End [FunctionName] log.", where FunctionName is the name of the calling function, and adds it to the log variable.
.EXAMPLE
Write-AuditLog -End -OutputPath "C:\Logs\auditlog.csv"
Sets the message to "End Log", adds it to the log variable, and exports the log to a CSV file.
.NOTES
Author: DrIOSx
#>
[CmdletBinding(DefaultParameterSetName = 'Default')]
param(
###
[Parameter(
Mandatory = $false,
HelpMessage = 'Input a Message string.',
Position = 0,
ParameterSetName = 'Default',
ValueFromPipeline = $true
)]
[ValidateNotNullOrEmpty()]
[string]$Message,
###
[Parameter(
Mandatory = $false,
HelpMessage = 'Information, Warning or Error.',
Position = 1,
ParameterSetName = 'Default',
ValueFromPipelineByPropertyName = $true
)]
[ValidateNotNullOrEmpty()]
[ValidateSet('Information', 'Warning', 'Error')]
[string]$Severity = 'Information',
###
[Parameter(
Mandatory = $false,
ParameterSetName = 'End'
)]
[switch]$End,
###
[Parameter(
Mandatory = $false,
ParameterSetName = 'BeginFunction'
)]
[switch]$BeginFunction,
[Parameter(
Mandatory = $false,
ParameterSetName = 'EndFunction'
)]
[switch]$EndFunction,
###
[Parameter(
Mandatory = $false,
ParameterSetName = 'Start'
)]
[switch]$Start,
###
[Parameter(
Mandatory = $false,
ParameterSetName = 'End'
)]
[string]$OutputPath
)
begin {
$ErrorActionPreference = "SilentlyContinue"
# Define variables to hold information about the command that was invoked.
$ModuleName = $Script:MyInvocation.MyCommand.Name -replace '\..*'
$callStack = Get-PSCallStack
if ($callStack.Count -gt 1) {
$FuncName = $callStack[1].Command
} else {
$FuncName = "DirectCall" # Or any other default name you prefer
}
#Write-Verbose "Funcname Name is $FuncName!" -Verbose
$ModuleVer = $MyInvocation.MyCommand.Version.ToString()
# Set the error action preference to continue.
$ErrorActionPreference = "Continue"
}
process {
try {
if (-not $Start -and -not (Test-Path variable:script:LogString)) {
throw "The logging variable is not initialized. Please call Write-AuditLog with the -Start switch or ensure $script:LogString is set."
}
$Function = $($FuncName + '.v' + $ModuleVer)
if ($Start) {
$script:LogString = @()
$Message = '+++ Begin Log | ' + $Function + ' |'
}
elseif ($BeginFunction) {
$Message = '>>> Begin Function Log | ' + $Function + ' |'
}
$logEntry = [pscustomobject]@{
Time = ((Get-Date).ToString('yyyy-MM-dd hh:mmTss'))
Module = $ModuleName
PSVersion = ($PSVersionTable.PSVersion).ToString()
PSEdition = ($PSVersionTable.PSEdition).ToString()
IsAdmin = $(Test-IsAdmin)
User = "$Env:USERDOMAIN\$Env:USERNAME"
HostName = $Env:COMPUTERNAME
InvokedBy = $Function
Severity = $Severity
Message = $Message
RunID = -1
}
if ($BeginFunction) {
$maxRunID = ($script:LogString | Where-Object { $_.InvokedBy -eq $Function } | Measure-Object -Property RunID -Maximum).Maximum
if ($null -eq $maxRunID) { $maxRunID = -1 }
$logEntry.RunID = $maxRunID + 1
}
else {
$lastRunID = ($script:LogString | Where-Object { $_.InvokedBy -eq $Function } | Select-Object -Last 1).RunID
if ($null -eq $lastRunID) { $lastRunID = 0 }
$logEntry.RunID = $lastRunID
}
if ($EndFunction) {
$FunctionStart = "$((($script:LogString | Where-Object {$_.InvokedBy -eq $Function -and $_.RunId -eq $lastRunID } | Sort-Object Time)[0]).Time)"
$startTime = ([DateTime]::ParseExact("$FunctionStart", 'yyyy-MM-dd hh:mmTss', $null))
$endTime = Get-Date
$timeTaken = $endTime - $startTime
$Message = '<<< End Function Log | ' + $Function + ' | Runtime: ' + "$($timeTaken.Minutes) min $($timeTaken.Seconds) sec"
$logEntry.Message = $Message
}
elseif ($End) {
$startTime = ([DateTime]::ParseExact($($script:LogString[0].Time), 'yyyy-MM-dd hh:mmTss', $null))
$endTime = Get-Date
$timeTaken = $endTime - $startTime
$Message = '--- End Log | ' + $Function + ' | Runtime: ' + "$($timeTaken.Minutes) min $($timeTaken.Seconds) sec"
$logEntry.Message = $Message
}
$script:LogString += $logEntry
switch ($Severity) {
'Warning' {
Write-Warning ('[WARNING] ! ' + $Message)
$UserInput = Read-Host "Warning encountered! Do you want to continue? (Y/N)"
if ($UserInput -eq 'N') {
throw "Script execution stopped by user."
}
}
'Error' { Write-Error ('[ERROR] X - ' + $FuncName + ' ' + $Message) -ErrorAction Continue }
'Verbose' { Write-Verbose ('[VERBOSE] ~ ' + $Message) }
Default { Write-Information ('[INFO] * ' + $Message) -InformationAction Continue}
}
}
catch {
throw "Write-AuditLog encountered an error (process block): $($_)"
}
}
end {
try {
if ($End) {
if (-not [string]::IsNullOrEmpty($OutputPath)) {
$script:LogString | Export-Csv -Path $OutputPath -NoTypeInformation
Write-Verbose "LogPath: $(Split-Path -Path $OutputPath -Parent)"
}
else {
throw "OutputPath is not specified for End action."
}
}
}
catch {
throw "Error in Write-AuditLog (end block): $($_.Exception.Message)"
}
}
}

View File

@@ -0,0 +1,88 @@
<#
.SYNOPSIS
Retrieves user licenses and roles for administrative accounts from Microsoft 365 via the Graph API.
.DESCRIPTION
The Get-AdminRoleUserLicense function connects to Microsoft Graph and retrieves all users who are assigned administrative roles along with their user details and licenses. This function is useful for auditing and compliance checks to ensure that administrators have appropriate licenses and role assignments.
.PARAMETER SkipGraphConnection
A switch parameter that, when set, skips the connection to Microsoft Graph if already established. This is useful for batch processing or when used within scripts where multiple calls are made and the connection is managed externally.
.EXAMPLE
PS> Get-AdminRoleUserLicense
This example retrieves all administrative role users along with their licenses by connecting to Microsoft Graph using the default scopes.
.EXAMPLE
PS> Get-AdminRoleUserLicense -SkipGraphConnection
This example retrieves all administrative role users along with their licenses without attempting to connect to Microsoft Graph, assuming that the connection is already established.
.INPUTS
None. You cannot pipe objects to Get-AdminRoleUserLicense.
.OUTPUTS
PSCustomObject
Returns a custom object for each user with administrative roles that includes the following properties: RoleName, UserName, UserPrincipalName, UserId, HybridUser, and Licenses.
.NOTES
Creation Date: 2024-04-15
Purpose/Change: Initial function development to support Microsoft 365 administrative role auditing.
.LINK
https://criticalsolutionsnetwork.github.io/M365FoundationsCISReport/#Get-AdminRoleUserLicense
#>
function Get-AdminRoleUserLicense {
# Set output type to System.Collections.ArrayList
[OutputType([System.Collections.ArrayList])]
[CmdletBinding()]
param (
[Parameter(Mandatory = $false)]
[switch]$SkipGraphConnection
)
begin {
if (-not $SkipGraphConnection) {
Connect-MgGraph -Scopes "Directory.Read.All", "Domain.Read.All", "Policy.Read.All", "Organization.Read.All" -NoWelcome
}
$adminRoleUsers = [System.Collections.ArrayList]::new()
$userIds = [System.Collections.ArrayList]::new()
}
Process {
$adminroles = Get-MgRoleManagementDirectoryRoleDefinition | Where-Object { $_.DisplayName -like "*Admin*" }
foreach ($role in $adminroles) {
$usersInRole = Get-MgRoleManagementDirectoryRoleAssignment -Filter "roleDefinitionId eq '$($role.Id)'"
foreach ($user in $usersInRole) {
$userDetails = Get-MgUser -UserId $user.PrincipalId -Property "DisplayName, UserPrincipalName, Id, onPremisesSyncEnabled" -ErrorAction SilentlyContinue
if ($userDetails) {
[void]($userIds.Add($user.PrincipalId))
[void](
$adminRoleUsers.Add(
[PSCustomObject]@{
RoleName = $role.DisplayName
UserName = $userDetails.DisplayName
UserPrincipalName = $userDetails.UserPrincipalName
UserId = $userDetails.Id
HybridUser = $userDetails.onPremisesSyncEnabled
Licenses = $null # Initialize as $null
}
)
)
}
}
}
foreach ($userId in $userIds.ToArray() | Select-Object -Unique) {
$licenses = Get-MgUserLicenseDetail -UserId $userId -ErrorAction SilentlyContinue
if ($licenses) {
$licenseList = ($licenses.SkuPartNumber -join '|')
$adminRoleUsers.ToArray() | Where-Object { $_.UserId -eq $userId } | ForEach-Object {
$_.Licenses = $licenseList
}
}
}
}
End {
Write-Host "Disconnecting from Microsoft Graph..." -ForegroundColor Green
Disconnect-MgGraph | Out-Null
return $adminRoleUsers
}
}

View File

@@ -4,9 +4,9 @@
.DESCRIPTION .DESCRIPTION
The Invoke-M365SecurityAudit cmdlet performs a comprehensive security audit based on the specified parameters. It allows auditing of various configurations and settings within a Microsoft 365 environment, such as compliance with CIS benchmarks. The Invoke-M365SecurityAudit cmdlet performs a comprehensive security audit based on the specified parameters. It allows auditing of various configurations and settings within a Microsoft 365 environment, such as compliance with CIS benchmarks.
.PARAMETER TenantAdminUrl .PARAMETER TenantAdminUrl
The URL of the tenant admin. This parameter is mandatory. The URL of the tenant admin. If not specified, none of the SharePoint Online tests will run.
.PARAMETER DomainName .PARAMETER M365DomainForPWPolicyTest
The domain name of the Microsoft 365 environment. This parameter is mandatory. The domain name of the Microsoft 365 environment to test. This parameter is not mandatory and by default it will pass/fail all found domains as a group if a specific domain is not specified.
.PARAMETER ELevel .PARAMETER ELevel
Specifies the E-Level (E3 or E5) for the audit. This parameter is optional and can be combined with the ProfileLevel parameter. Specifies the E-Level (E3 or E5) for the audit. This parameter is optional and can be combined with the ProfileLevel parameter.
.PARAMETER ProfileLevel .PARAMETER ProfileLevel
@@ -28,70 +28,127 @@
.PARAMETER NoModuleCheck .PARAMETER NoModuleCheck
If specified, the cmdlet will not check for the presence of required modules. If specified, the cmdlet will not check for the presence of required modules.
.EXAMPLE .EXAMPLE
PS> Invoke-M365SecurityAudit -TenantAdminUrl "https://contoso-admin.sharepoint.com" -DomainName "contoso.com" -ELevel "E5" -ProfileLevel "L1" PS> Invoke-M365SecurityAudit
Performs a security audit using default parameters.
Output:
Status : Fail
ELevel : E3
ProfileLevel: L1
Connection : Microsoft Graph
Rec : 1.1.1
Result : False
Details : Non-compliant accounts:
Username | Roles | HybridStatus | Missing Licence
user1@domain.com| Global Administrator | Cloud-Only | AAD_PREMIUM
user2@domain.com| Global Administrator | Hybrid | AAD_PREMIUM, AAD_PREMIUM_P2
FailureReason: Non-Compliant Accounts: 2
.EXAMPLE
PS> Invoke-M365SecurityAudit -TenantAdminUrl "https://contoso-admin.sharepoint.com" -M365DomainForPWPolicyTest "contoso.com" -ELevel "E5" -ProfileLevel "L1"
Performs a security audit for the E5 level and L1 profile in the specified Microsoft 365 environment. Performs a security audit for the E5 level and L1 profile in the specified Microsoft 365 environment.
Output:
Status : Fail
ELevel : E5
ProfileLevel: L1
Connection : Microsoft Graph
Rec : 1.1.1
Result : False
Details : Non-compliant accounts:
Username | Roles | HybridStatus | Missing Licence
user1@domain.com| Global Administrator | Cloud-Only | AAD_PREMIUM
user2@domain.com| Global Administrator | Hybrid | AAD_PREMIUM, AAD_PREMIUM_P2
FailureReason: Non-Compliant Accounts: 2
.EXAMPLE .EXAMPLE
PS> Invoke-M365SecurityAudit -TenantAdminUrl "https://contoso-admin.sharepoint.com" -DomainName "contoso.com" -IncludeIG1 PS> Invoke-M365SecurityAudit -TenantAdminUrl "https://contoso-admin.sharepoint.com" -M365DomainForPWPolicyTest "contoso.com" -IncludeIG1
Performs an audit including all tests where IG1 is true. Performs an audit including all tests where IG1 is true.
Output:
Status : Fail
ELevel : E3
ProfileLevel: L1
Connection : Microsoft Graph
Rec : 1.1.1
Result : False
Details : Non-compliant accounts:
Username | Roles | HybridStatus | Missing Licence
user1@domain.com| Global Administrator | Cloud-Only | AAD_PREMIUM
user2@domain.com| Global Administrator | Hybrid | AAD_PREMIUM, AAD_PREMIUM_P2
FailureReason: Non-Compliant Accounts: 2
.EXAMPLE .EXAMPLE
PS> Invoke-M365SecurityAudit -TenantAdminUrl "https://contoso-admin.sharepoint.com" -DomainName "contoso.com" -SkipRecommendation '1.1.3', '2.1.1' PS> Invoke-M365SecurityAudit -TenantAdminUrl "https://contoso-admin.sharepoint.com" -M365DomainForPWPolicyTest "contoso.com" -SkipRecommendation '1.1.3', '2.1.1'
Performs an audit while excluding specific recommendations 1.1.3 and 2.1.1. Performs an audit while excluding specific recommendations 1.1.3 and 2.1.1.
Output:
Status : Fail
ELevel : E3
ProfileLevel: L1
Connection : Microsoft Graph
Rec : 1.1.1
Result : False
Details : Non-compliant accounts:
Username | Roles | HybridStatus | Missing Licence
user1@domain.com| Global Administrator | Cloud-Only | AAD_PREMIUM
user2@domain.com| Global Administrator | Hybrid | AAD_PREMIUM, AAD_PREMIUM_P2
FailureReason: Non-Compliant Accounts: 2
.EXAMPLE .EXAMPLE
PS> $auditResults = Invoke-M365SecurityAudit -TenantAdminUrl "https://contoso-admin.sharepoint.com" -DomainName "contoso.com" PS> $auditResults = Invoke-M365SecurityAudit -TenantAdminUrl "https://contoso-admin.sharepoint.com" -M365DomainForPWPolicyTest "contoso.com"
PS> $auditResults | Export-Csv -Path "auditResults.csv" -NoTypeInformation PS> $auditResults | Export-Csv -Path "auditResults.csv" -NoTypeInformation
Captures the audit results into a variable and exports them to a CSV file. Captures the audit results into a variable and exports them to a CSV file.
Output:
CISAuditResult[]
auditResults.csv
.EXAMPLE
PS> Invoke-M365SecurityAudit -WhatIf
Displays what would happen if the cmdlet is run without actually performing the audit.
Output:
What if: Performing the operation "Invoke-M365SecurityAudit" on target "Microsoft 365 environment".
.INPUTS .INPUTS
None. You cannot pipe objects to Invoke-M365SecurityAudit. None. You cannot pipe objects to Invoke-M365SecurityAudit.
.OUTPUTS .OUTPUTS
CISAuditResult[] CISAuditResult[]
The cmdlet returns an array of CISAuditResult objects representing the results of the security audit. The cmdlet returns an array of CISAuditResult objects representing the results of the security audit.
.NOTES .NOTES
- This module is based on CIS benchmarks. - This module is based on CIS benchmarks.
- Governed by the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. - Governed by the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
- Commercial use is not permitted. This module cannot be sold or used for commercial purposes. - Commercial use is not permitted. This module cannot be sold or used for commercial purposes.
- Modifications and sharing are allowed under the same license. - Modifications and sharing are allowed under the same license.
- For full license details, visit: https://creativecommons.org/licenses/by-nc-sa/4.0/deed.en - For full license details, visit: https://creativecommons.org/licenses/by-nc-sa/4.0/deed.en
- Register for CIS Benchmarks at: https://www.cisecurity.org/cis-benchmarks
.LINK .LINK
Online Version: [GitHub Repository URL] https://criticalsolutionsnetwork.github.io/M365FoundationsCISReport/#Invoke-M365SecurityAudit
#> #>
function Invoke-M365SecurityAudit { function Invoke-M365SecurityAudit {
[CmdletBinding(SupportsShouldProcess = $true, DefaultParameterSetName = 'Default')] [CmdletBinding(SupportsShouldProcess = $true, DefaultParameterSetName = 'Default')]
[OutputType([CISAuditResult[]])] [OutputType([CISAuditResult[]])]
param ( param (
[Parameter(Mandatory = $true)] [Parameter(Mandatory = $false, HelpMessage = "The SharePoint tenant admin URL, which should end with '-admin.sharepoint.com'. If not specified none of the Sharepoint Online tests will run.")]
[ValidatePattern('^https://[a-zA-Z0-9-]+-admin\.sharepoint\.com$')]
[string]$TenantAdminUrl, [string]$TenantAdminUrl,
[Parameter(Mandatory = $true)] [Parameter(Mandatory = $false, HelpMessage = "Specify this to test only the default domain for password expiration policy when '1.3.1' is included in the tests to be run. The domain name of your organization, e.g., 'example.com'.")]
[string]$DomainName, [ValidatePattern('^[a-zA-Z0-9-]+\.[a-zA-Z]{2,}$')]
[string]$M365DomainForPWPolicyTest,
# E-Level with optional ProfileLevel selection # E-Level with optional ProfileLevel selection
[Parameter(ParameterSetName = 'ELevelFilter')] [Parameter(Mandatory = $true, ParameterSetName = 'ELevelFilter')]
[ValidateSet('E3', 'E5')] [ValidateSet('E3', 'E5')]
[string]$ELevel, [string]$ELevel,
[Parameter(ParameterSetName = 'ELevelFilter')] [Parameter(Mandatory = $true, ParameterSetName = 'ELevelFilter')]
[ValidateSet('L1', 'L2')] [ValidateSet('L1', 'L2')]
[string]$ProfileLevel, [string]$ProfileLevel,
# IG Filters, one at a time # IG Filters, one at a time
[Parameter(ParameterSetName = 'IG1Filter')] [Parameter(Mandatory = $true, ParameterSetName = 'IG1Filter')]
[switch]$IncludeIG1, [switch]$IncludeIG1,
[Parameter(ParameterSetName = 'IG2Filter')] [Parameter(Mandatory = $true, ParameterSetName = 'IG2Filter')]
[switch]$IncludeIG2, [switch]$IncludeIG2,
[Parameter(ParameterSetName = 'IG3Filter')] [Parameter(Mandatory = $true, ParameterSetName = 'IG3Filter')]
[switch]$IncludeIG3, [switch]$IncludeIG3,
# Inclusion of specific recommendation numbers # Inclusion of specific recommendation numbers
[Parameter(ParameterSetName = 'RecFilter')] [Parameter(Mandatory = $true, ParameterSetName = 'RecFilter')]
[ValidateSet( [ValidateSet(
'1.1.3', '1.2.1', '1.2.2', '1.3.1', '1.3.3', '1.3.6', '2.1.1', '2.1.2', ` '1.1.1', '1.1.3', '1.2.1', '1.2.2', '1.3.1', '1.3.3', '1.3.6', '2.1.1', '2.1.2', `
'2.1.3', '2.1.4', '2.1.5', '2.1.6', '2.1.7', '2.1.9', '3.1.1', '5.1.2.3', ` '2.1.3', '2.1.4', '2.1.5', '2.1.6', '2.1.7', '2.1.9', '3.1.1', '5.1.2.3', `
'5.1.8.1', '6.1.1', '6.1.2', '6.1.3', '6.2.1', '6.2.2', '6.2.3', '6.3.1', ` '5.1.8.1', '6.1.1', '6.1.2', '6.1.3', '6.2.1', '6.2.2', '6.2.3', '6.3.1', `
'6.5.1', '6.5.2', '6.5.3', '7.2.1', '7.2.10', '7.2.2', '7.2.3', '7.2.4', ` '6.5.1', '6.5.2', '6.5.3', '7.2.1', '7.2.10', '7.2.2', '7.2.3', '7.2.4', `
@@ -102,9 +159,9 @@ function Invoke-M365SecurityAudit {
[string[]]$IncludeRecommendation, [string[]]$IncludeRecommendation,
# Exclusion of specific recommendation numbers # Exclusion of specific recommendation numbers
[Parameter(ParameterSetName = 'SkipRecFilter')] [Parameter(Mandatory = $true, ParameterSetName = 'SkipRecFilter')]
[ValidateSet( [ValidateSet(
'1.1.3', '1.2.1', '1.2.2', '1.3.1', '1.3.3', '1.3.6', '2.1.1', '2.1.2', ` '1.1.1', '1.1.3', '1.2.1', '1.2.2', '1.3.1', '1.3.3', '1.3.6', '2.1.1', '2.1.2', `
'2.1.3', '2.1.4', '2.1.5', '2.1.6', '2.1.7', '2.1.9', '3.1.1', '5.1.2.3', ` '2.1.3', '2.1.4', '2.1.5', '2.1.6', '2.1.7', '2.1.9', '3.1.1', '5.1.2.3', `
'5.1.8.1', '6.1.1', '6.1.2', '6.1.3', '6.2.1', '6.2.2', '6.2.3', '6.3.1', ` '5.1.8.1', '6.1.1', '6.1.2', '6.1.3', '6.2.1', '6.2.2', '6.2.3', '6.3.1', `
'6.5.1', '6.5.2', '6.5.3', '7.2.1', '7.2.10', '7.2.2', '7.2.3', '7.2.4', ` '6.5.1', '6.5.2', '6.5.3', '7.2.1', '7.2.10', '7.2.2', '7.2.3', '7.2.4', `
@@ -120,139 +177,117 @@ function Invoke-M365SecurityAudit {
[switch]$NoModuleCheck [switch]$NoModuleCheck
) )
Begin { Begin {
if ($script:MaximumFunctionCount -lt 8192) { if ($script:MaximumFunctionCount -lt 8192) {
$script:MaximumFunctionCount = 8192 $script:MaximumFunctionCount = 8192
} }
# Ensure required modules are installed # Ensure required modules are installed
# Define the required modules and versions in a hashtable $requiredModules = Get-RequiredModule -AuditFunction
if (!($NoModuleCheck)) {
$requiredModules = @( # Format the required modules list
@{ ModuleName = "ExchangeOnlineManagement"; RequiredVersion = "3.3.0" }, $requiredModulesFormatted = Format-RequiredModuleList -RequiredModules $requiredModules
@{ ModuleName = "AzureAD"; RequiredVersion = "2.0.2.182" },
@{ ModuleName = "Microsoft.Graph"; RequiredVersion = "2.4.0"; SubModuleName = "Authentication" }, # Check and install required modules if necessary
@{ ModuleName = "Microsoft.Graph"; RequiredVersion = "2.4.0"; SubModuleName = "Users" }, if (!($NoModuleCheck) -and $PSCmdlet.ShouldProcess("Check for required modules: $requiredModulesFormatted", "Check")) {
@{ ModuleName = "Microsoft.Graph"; RequiredVersion = "2.4.0"; SubModuleName = "Groups" },
@{ ModuleName = "Microsoft.Graph"; RequiredVersion = "2.4.0"; SubModuleName = "DirectoryObjects" },
@{ ModuleName = "Microsoft.Graph"; RequiredVersion = "2.4.0"; SubModuleName = "Domains" },
@{ ModuleName = "Microsoft.Graph"; RequiredVersion = "2.4.0"; SubModuleName = "Reports" },
@{ ModuleName = "Microsoft.Graph"; RequiredVersion = "2.4.0"; SubModuleName = "Mail" },
@{ ModuleName = "Microsoft.Online.SharePoint.PowerShell"; RequiredVersion = "16.0.24009.12000" },
@{ ModuleName = "MicrosoftTeams"; RequiredVersion = "5.5.0" }
)
foreach ($module in $requiredModules) { foreach ($module in $requiredModules) {
Assert-ModuleAvailability -ModuleName $module.ModuleName -RequiredVersion $module.RequiredVersion -SubModuleName $module.SubModuleName Assert-ModuleAvailability -ModuleName $module.ModuleName -RequiredVersion $module.RequiredVersion -SubModules $module.SubModules
} }
} }
# Loop through each required module and assert its availability
# Establishing connections
#if (!($DoNotConnect -or $DoNotTest)) {
# Establishing connections
if (!($DoNotConnect)) {
Connect-M365Suite -TenantAdminUrl $TenantAdminUrl
}
# Load test definitions from CSV # Load test definitions from CSV
$testDefinitionsPath = Join-Path -Path $PSScriptRoot -ChildPath "helper\TestDefinitions.csv" $testDefinitionsPath = Join-Path -Path $PSScriptRoot -ChildPath "helper\TestDefinitions.csv"
$testDefinitions = Import-Csv -Path $testDefinitionsPath $testDefinitions = Import-Csv -Path $testDefinitionsPath
# Load the Test Definitions into the script scope for use in other functions
$script:TestDefinitionsObject = $testDefinitions
# Apply filters based on parameter sets # Apply filters based on parameter sets
switch ($PSCmdlet.ParameterSetName) { $params = @{
'ELevelFilter' { TestDefinitions = $testDefinitions
if ($null -ne $ELevel -and $null -ne $ProfileLevel) { ParameterSetName = $PSCmdlet.ParameterSetName
$testDefinitions = $testDefinitions | Where-Object { ELevel = $ELevel
$_.ELevel -eq $ELevel -and $_.ProfileLevel -eq $ProfileLevel ProfileLevel = $ProfileLevel
} IncludeRecommendation = $IncludeRecommendation
SkipRecommendation = $SkipRecommendation
}
$testDefinitions = Get-TestDefinitionsObject @params
# Extract unique connections needed
$requiredConnections = $testDefinitions.Connection | Sort-Object -Unique
if ($requiredConnections -contains 'SPO') {
if (-not $TenantAdminUrl) {
$requiredConnections = $requiredConnections | Where-Object { $_ -ne 'SPO' }
$testDefinitions = $testDefinitions | Where-Object { $_.Connection -ne 'SPO' }
if ($null -eq $testDefinitions) {
throw "No tests to run as no SharePoint Online tests are available."
} }
elseif ($null -ne $ELevel) {
$testDefinitions = $testDefinitions | Where-Object {
$_.ELevel -eq $ELevel
}
}
elseif ($null -ne $ProfileLevel) {
$testDefinitions = $testDefinitions | Where-Object {
$_.ProfileLevel -eq $ProfileLevel
}
}
}
'IG1Filter' {
$testDefinitions = $testDefinitions | Where-Object { $_.IG1 -eq 'TRUE' }
}
'IG2Filter' {
$testDefinitions = $testDefinitions | Where-Object { $_.IG2 -eq 'TRUE' }
}
'IG3Filter' {
$testDefinitions = $testDefinitions | Where-Object { $_.IG3 -eq 'TRUE' }
}
'RecFilter' {
$testDefinitions = $testDefinitions | Where-Object { $IncludeRecommendation -contains $_.Rec }
}
'SkipRecFilter' {
$testDefinitions = $testDefinitions | Where-Object { $SkipRecommendation -notcontains $_.Rec }
} }
} }
# End switch ($PSCmdlet.ParameterSetName)
# Determine which test files to load based on filtering # Determine which test files to load based on filtering
$testsToLoad = $testDefinitions.TestFileName | ForEach-Object { $_ -replace '.ps1$', '' } $testsToLoad = $testDefinitions.TestFileName | ForEach-Object { $_ -replace '.ps1$', '' }
# Display the tests that would be loaded if the function is called with -WhatIf
Write-Verbose "The $(($testsToLoad).count) test/s that would be loaded based on filter criteria:" Write-Verbose "The $(($testsToLoad).count) test/s that would be loaded based on filter criteria:"
$testsToLoad | ForEach-Object { Write-Verbose " $_" } $testsToLoad | ForEach-Object { Write-Verbose " $_" }
# Initialize a collection to hold failed test details
$script:FailedTests = [System.Collections.ArrayList]::new()
} # End Begin } # End Begin
Process { Process {
$allAuditResults = @() # Initialize a collection to hold all results $allAuditResults = [System.Collections.ArrayList]::new() # Initialize a collection to hold all results
# Dynamically dot-source the test scripts # Dynamically dot-source the test scripts
$testsFolderPath = Join-Path -Path $PSScriptRoot -ChildPath "tests" $testsFolderPath = Join-Path -Path $PSScriptRoot -ChildPath "tests"
$testFiles = Get-ChildItem -Path $testsFolderPath -Filter "Test-*.ps1" | $testFiles = Get-ChildItem -Path $testsFolderPath -Filter "Test-*.ps1" |
Where-Object { $testsToLoad -contains $_.BaseName } Where-Object { $testsToLoad -contains $_.BaseName }
$totalTests = $testFiles.Count
$currentTestIndex = 0
# Establishing connections if required
$actualUniqueConnections = Get-UniqueConnection -Connections $requiredConnections
if (!($DoNotConnect) -and $PSCmdlet.ShouldProcess("Establish connections to Microsoft 365 services: $($actualUniqueConnections -join ', ')", "Connect")) {
Write-Information "Establishing connections to Microsoft 365 services: $($actualUniqueConnections -join ', ')" -InformationAction Continue
Connect-M365Suite -TenantAdminUrl $TenantAdminUrl -RequiredConnections $requiredConnections
}
Write-Information "A total of $($totalTests) tests were selected to run..." -InformationAction Continue
# Import the test functions # Import the test functions
$testFiles | ForEach-Object { $testFiles | ForEach-Object {
$currentTestIndex++
Write-Progress -Activity "Loading Test Scripts" -Status "Loading $($currentTestIndex) of $($totalTests): $($_.Name)" -PercentComplete (($currentTestIndex / $totalTests) * 100)
Try { Try {
# Dot source the test function
. $_.FullName . $_.FullName
} }
Catch { Catch {
# Log the error and add the test to the failed tests collection
Write-Error "Failed to load test function $($_.Name): $_" Write-Error "Failed to load test function $($_.Name): $_"
$script:FailedTests.Add([PSCustomObject]@{ Test = $_.Name; Error = $_ })
} }
} }
$currentTestIndex = 0
# Execute each test function from the prepared list # Execute each test function from the prepared list
foreach ($testFunction in $testFiles) { foreach ($testFunction in $testFiles) {
$currentTestIndex++
Write-Progress -Activity "Executing Tests" -Status "Executing $($currentTestIndex) of $($totalTests): $($testFunction.Name)" -PercentComplete (($currentTestIndex / $totalTests) * 100)
$functionName = $testFunction.BaseName $functionName = $testFunction.BaseName
$functionCmd = Get-Command -Name $functionName
# Check if the test function needs DomainName parameter
$paramList = @{}
if ('DomainName' -in $functionCmd.Parameters.Keys) {
$paramList.DomainName = $DomainName
}
# Use splatting to pass parameters
if ($PSCmdlet.ShouldProcess($functionName, "Execute test")) { if ($PSCmdlet.ShouldProcess($functionName, "Execute test")) {
Write-Host "Running $functionName..." $auditResult = Invoke-TestFunction -FunctionFile $testFunction -DomainName $M365DomainForPWPolicyTest
$result = & $functionName @paramList # Add the result to the collection
# Assuming each function returns an array of CISAuditResult or a single CISAuditResult [void]$allAuditResults.Add($auditResult)
$allAuditResults += $result
} }
} }
} }
End { End {
# Return all collected audit results if (!($DoNotDisconnect) -and $PSCmdlet.ShouldProcess("Disconnect from Microsoft 365 services: $($actualUniqueConnections -join ', ')", "Disconnect")) {
return $allAuditResults
# Check if the Disconnect switch is present
if (!($DoNotDisconnect)) {
# Clean up sessions # Clean up sessions
Disconnect-M365Suite Disconnect-M365Suite -RequiredConnections $requiredConnections
}
if ($PSCmdlet.ShouldProcess("Measure and display audit results for $($totalTests) tests", "Measure")) {
# Call the private function to calculate and display results
Measure-AuditResult -AllAuditResults $allAuditResults -FailedTests $script:FailedTests
# Return all collected audit results
return $allAuditResults.ToArray() | Sort-Object -Property Rec
} }
} }
} }

View File

@@ -0,0 +1,90 @@
<#
.SYNOPSIS
Synchronizes data between an Excel file and either a CSV file or an output object from Invoke-M365SecurityAudit, and optionally updates the Excel worksheet.
.DESCRIPTION
The Sync-CISExcelAndCsvData function merges data from a specified Excel file with data from either a CSV file or an output object from Invoke-M365SecurityAudit based on a common key. It can also update the Excel worksheet with the merged data. This function is particularly useful for updating Excel records with additional data from a CSV file or audit results while preserving the original formatting and structure of the Excel worksheet.
.PARAMETER ExcelPath
The path to the Excel file that contains the original data. This parameter is mandatory.
.PARAMETER WorksheetName
The name of the worksheet within the Excel file that contains the data to be synchronized. This parameter is mandatory.
.PARAMETER CsvPath
The path to the CSV file containing data to be merged with the Excel data. This parameter is mandatory when using the CsvInput parameter set.
.PARAMETER AuditResults
An array of CISAuditResult objects from Invoke-M365SecurityAudit to be merged with the Excel data. This parameter is mandatory when using the ObjectInput parameter set. It can also accept pipeline input.
.PARAMETER SkipUpdate
If specified, the function will return the merged data object without updating the Excel worksheet. This is useful for previewing the merged data.
.EXAMPLE
PS> Sync-CISExcelAndCsvData -ExcelPath "path\to\excel.xlsx" -WorksheetName "DataSheet" -CsvPath "path\to\data.csv"
Merges data from 'data.csv' into 'excel.xlsx' on the 'DataSheet' worksheet and updates the worksheet with the merged data.
.EXAMPLE
PS> $mergedData = Sync-CISExcelAndCsvData -ExcelPath "path\to\excel.xlsx" -WorksheetName "DataSheet" -CsvPath "path\to\data.csv" -SkipUpdate
Retrieves the merged data object for preview without updating the Excel worksheet.
.EXAMPLE
PS> $auditResults = Invoke-M365SecurityAudit -TenantAdminUrl "https://tenant-admin.url" -DomainName "example.com"
PS> Sync-CISExcelAndCsvData -ExcelPath "path\to\excel.xlsx" -WorksheetName "DataSheet" -AuditResults $auditResults
Merges data from the audit results into 'excel.xlsx' on the 'DataSheet' worksheet and updates the worksheet with the merged data.
.EXAMPLE
PS> $auditResults = Invoke-M365SecurityAudit -TenantAdminUrl "https://tenant-admin.url" -DomainName "example.com"
PS> $mergedData = Sync-CISExcelAndCsvData -ExcelPath "path\to\excel.xlsx" -WorksheetName "DataSheet" -AuditResults $auditResults -SkipUpdate
Retrieves the merged data object for preview without updating the Excel worksheet.
.EXAMPLE
PS> Invoke-M365SecurityAudit -TenantAdminUrl "https://tenant-admin.url" -DomainName "example.com" | Sync-CISExcelAndCsvData -ExcelPath "path\to\excel.xlsx" -WorksheetName "DataSheet"
Pipes the audit results into Sync-CISExcelAndCsvData to merge data into 'excel.xlsx' on the 'DataSheet' worksheet and updates the worksheet with the merged data.
.INPUTS
System.String, CISAuditResult[]
You can pipe CISAuditResult objects to Sync-CISExcelAndCsvData.
.OUTPUTS
Object[]
If the SkipUpdate switch is used, the function returns an array of custom objects representing the merged data.
.NOTES
- Ensure that the 'ImportExcel' module is installed and up to date.
- It is recommended to backup the Excel file before running this script to prevent accidental data loss.
- This function is part of the CIS Excel and CSV Data Management Toolkit.
.LINK
https://criticalsolutionsnetwork.github.io/M365FoundationsCISReport/#Sync-CISExcelAndCsvData
#>
function Sync-CISExcelAndCsvData {
[OutputType([void], [PSCustomObject[]])]
[CmdletBinding(DefaultParameterSetName = 'CsvInput')]
param (
[Parameter(Mandatory = $true)]
[ValidateScript({ Test-Path $_ })]
[string]$ExcelPath,
[Parameter(Mandatory = $true)]
[string]$WorksheetName,
[Parameter(Mandatory = $true, ParameterSetName = 'CsvInput')]
[ValidateScript({ Test-Path $_ })]
[string]$CsvPath,
[Parameter(Mandatory = $true, ParameterSetName = 'ObjectInput', ValueFromPipeline = $true)]
[CISAuditResult[]]$AuditResults,
[Parameter(Mandatory = $false)]
[switch]$SkipUpdate
)
process {
# Verify ImportExcel module is available
$requiredModules = Get-RequiredModule -SyncFunction
foreach ($module in $requiredModules) {
Assert-ModuleAvailability -ModuleName $module.ModuleName -RequiredVersion $module.RequiredVersion -SubModuleName $module.SubModuleName
}
# Merge Excel and CSV data or Audit Results
if ($PSCmdlet.ParameterSetName -eq 'CsvInput') {
$mergedData = Merge-CISExcelAndCsvData -ExcelPath $ExcelPath -WorksheetName $WorksheetName -CsvPath $CsvPath
} else {
$mergedData = Merge-CISExcelAndCsvData -ExcelPath $ExcelPath -WorksheetName $WorksheetName -AuditResults $AuditResults
}
# Output the merged data if the user chooses to skip the update
if ($SkipUpdate) {
return $mergedData
} else {
# Update the Excel worksheet with the merged data
Update-CISExcelWorksheet -ExcelPath $ExcelPath -WorksheetName $WorksheetName -Data $mergedData
}
}
}

View File

@@ -1,51 +1,52 @@
Index,TestFileName,Rec,ELevel,ProfileLevel,IG1,IG2,IG3 Index,TestFileName,Rec,RecDescription,ELevel,ProfileLevel,CISControl,CISDescription,IG1,IG2,IG3,Automated,Connection
1,Test-AntiPhishingPolicy.ps1,2.1.7,E5,L1,FALSE,FALSE,TRUE 1,Test-AdministrativeAccountCompliance.ps1,1.1.1,Ensure Administrative accounts are separate and cloud-only,E3,L1,5.4,Restrict Administrator Privileges to Dedicated Administrator Accounts,TRUE,TRUE,TRUE,FALSE,Microsoft Graph
2,Test-AuditDisabledFalse.ps1,6.1.1,E3,L1,TRUE,TRUE,TRUE 2,Test-GlobalAdminsCount.ps1,1.1.3,Ensure that between two and four global admins are designated,E3,L1,5.1,Establish and Maintain an Inventory of Accounts,TRUE,TRUE,TRUE,TRUE,Microsoft Graph
3,Test-AuditLogSearch.ps1,3.1.1,E3,L1,TRUE,TRUE,TRUE 3,Test-ManagedApprovedPublicGroups.ps1,1.2.1,Ensure that only organizationally managed/approved public groups exist,E3,L2,3.3,Configure Data Access Control Lists,TRUE,TRUE,TRUE,TRUE,Microsoft Graph
4,Test-BlockChannelEmails.ps1,8.1.2,E3,L1,FALSE,FALSE,FALSE 4,Test-BlockSharedMailboxSignIn.ps1,1.2.2,Ensure sign-in to shared mailboxes is blocked,E3,L1,0,Explicitly Not Mapped,FALSE,FALSE,FALSE,TRUE,AzureAD | EXO
5,Test-BlockMailForwarding.ps1,6.2.1,E3,L1,FALSE,FALSE,FALSE 5,Test-PasswordNeverExpirePolicy.ps1,1.3.1,Ensure the 'Password expiration policy' is set to 'Set passwords to never expire',E3,L1,5.2,Use Unique Passwords,TRUE,TRUE,TRUE,TRUE,Microsoft Graph
6,Test-BlockSharedMailboxSignIn.ps1,1.2.2,E3,L1,FALSE,FALSE,FALSE 6,Test-ExternalSharingCalendars.ps1,1.3.3,Ensure 'External sharing' of calendars is not available,E3,L2,4.8,Uninstall or Disable Unnecessary Services on Enterprise Assets and Software,FALSE,TRUE,TRUE,TRUE,EXO
7,Test-CommonAttachmentFilter.ps1,2.1.2,E3,L1,FALSE,TRUE,TRUE 7,Test-CustomerLockbox.ps1,1.3.6,Ensure the customer lockbox feature is enabled,E5,L2,0,Explicitly Not Mapped,FALSE,FALSE,FALSE,TRUE,EXO
8,Test-CustomerLockbox.ps1,1.3.6,E5,L2,FALSE,FALSE,FALSE 8,Test-SafeLinksOfficeApps.ps1,2.1.1,Ensure Safe Links for Office Applications is Enabled,E5,L2,10.1,Deploy and Maintain Anti-Malware Software,TRUE,TRUE,TRUE,TRUE,EXO
9,Test-DialInBypassLobby.ps1,8.5.4,E3,L1,FALSE,FALSE,FALSE 9,Test-CommonAttachmentFilter.ps1,2.1.2,Ensure the Common Attachment Types Filter is enabled,E3,L1,9.6,Block Unnecessary File Types,FALSE,TRUE,TRUE,TRUE,EXO
10,Test-DisallowInfectedFilesDownload.ps1,7.3.1,E5,L2,TRUE,TRUE,TRUE 10,Test-NotifyMalwareInternal.ps1,2.1.3,Ensure notifications for internal users sending malware is Enabled,E3,L1,17.5,Assign Key Roles and Responsibilities,FALSE,TRUE,TRUE,TRUE,EXO
11,Test-EnableDKIM.ps1,2.1.9,E3,L1,FALSE,TRUE,TRUE 11,Test-SafeAttachmentsPolicy.ps1,2.1.4,Ensure Safe Attachments policy is enabled,E5,L2,9.7,Deploy and Maintain Email Server Anti-Malware Protections,FALSE,FALSE,TRUE,TRUE,EXO
12,Test-ExternalNoControl.ps1,8.5.7,E3,L1,FALSE,FALSE,FALSE 12,Test-SafeAttachmentsTeams.ps1,2.1.5,"Ensure Safe Attachments for SharePoint, OneDrive, and Microsoft Teams is Enabled",E5,L2,"9.7,10.1","Deploy and Maintain Email Server Anti-Malware Protections, Deploy and Maintain Anti-Malware Software",TRUE,TRUE,TRUE,TRUE,EXO
13,Test-ExternalSharingCalendars.ps1,1.3.3,E3,L2,FALSE,TRUE,TRUE 13,Test-SpamPolicyAdminNotify.ps1,2.1.6,Ensure Exchange Online Spam Policies are set to notify administrators,E3,L1,17.5,Assign Key Roles and Responsibilities,FALSE,TRUE,TRUE,TRUE,EXO
14,Test-GlobalAdminsCount.ps1,1.1.3,E3,L1,TRUE,TRUE,TRUE 14,Test-AntiPhishingPolicy.ps1,2.1.7,Ensure that an anti-phishing policy has been created,E5,L1,9.7,Deploy and Maintain Email Server Anti-Malware Protections,FALSE,FALSE,TRUE,TRUE,EXO
15,Test-GuestAccessExpiration.ps1,7.2.9,E3,L1,FALSE,FALSE,FALSE 15,Test-EnableDKIM.ps1,2.1.9,Ensure that DKIM is enabled for all Exchange Online Domains,E3,L1,9.5,Implement DMARC,FALSE,TRUE,TRUE,TRUE,EXO
16,Test-IdentifyExternalEmail.ps1,6.2.3,E3,L1,FALSE,FALSE,FALSE 16,Test-AuditLogSearch.ps1,3.1.1,Ensure Microsoft 365 audit log search is Enabled,E3,L1,8.2,Collect Audit Logs,TRUE,TRUE,TRUE,TRUE,EXO
17,Test-LinkSharingRestrictions.ps1,7.2.7,E3,L1,TRUE,TRUE,TRUE 17,Test-RestrictTenantCreation.ps1,5.1.2.3,Ensure 'Restrict non-admin users from creating tenants' is set to 'Yes',E3,L1,0,Explicitly Not Mapped,FALSE,FALSE,FALSE,TRUE,Microsoft Graph
18,Test-MailboxAuditingE3.ps1,6.1.2,E3,L1,TRUE,TRUE,TRUE 18,Test-PasswordHashSync.ps1,5.1.8.1,Ensure password hash sync is enabled for hybrid deployments,E3,L1,6.7,Centralize Access Control,FALSE,TRUE,TRUE,TRUE,Microsoft Graph
19,Test-MailboxAuditingE5.ps1,6.1.3,E5,L1,TRUE,TRUE,TRUE 19,Test-AuditDisabledFalse.ps1,6.1.1,Ensure 'AuditDisabled' organizationally is set to 'False',E3,L1,8.2,Collect Audit Logs,TRUE,TRUE,TRUE,TRUE,Microsoft Graph
20,Test-MailTipsEnabled.ps1,6.5.2,E3,L2,FALSE,FALSE,FALSE 20,Test-MailboxAuditingE3.ps1,6.1.2,Ensure mailbox auditing for Office E3 users is Enabled,E3,L1,8.2,Collect audit logs.,TRUE,TRUE,TRUE,TRUE,AzureAD | EXO | Microsoft Graph
21,Test-ManagedApprovedPublicGroups.ps1,1.2.1,E3,L2,TRUE,TRUE,TRUE 21,Test-MailboxAuditingE5.ps1,6.1.3,Ensure mailbox auditing for Office E5 users is Enabled,E5,L1,8.2,Collect audit logs.,TRUE,TRUE,TRUE,TRUE,AzureAD | EXO | Microsoft Graph
22,Test-MeetingChatNoAnonymous.ps1,8.5.5,E3,L1,FALSE,FALSE,FALSE 22,Test-BlockMailForwarding.ps1,6.2.1,Ensure all forms of mail forwarding are blocked and/or disabled,E3,L1,0,Explicitly Not Mapped,FALSE,FALSE,FALSE,TRUE,EXO
23,Test-ModernAuthExchangeOnline.ps1,6.5.1,E3,L1,FALSE,TRUE,TRUE 23,Test-NoWhitelistDomains.ps1,6.2.2,Ensure mail transport rules do not whitelist specific domains,E3,L1,0,Explicitly Not Mapped,FALSE,FALSE,FALSE,TRUE,EXO
24,Test-ModernAuthSharePoint.ps1,7.2.1,E3,L1,FALSE,TRUE,TRUE 24,Test-IdentifyExternalEmail.ps1,6.2.3,Ensure email from external senders is identified,E3,L1,0,Explicitly Not Mapped,FALSE,FALSE,FALSE,TRUE,EXO
25,Test-NoAnonymousMeetingJoin.ps1,8.5.1,E3,L2,FALSE,FALSE,FALSE 25,Test-RestrictOutlookAddins.ps1,6.3.1,Ensure users installing Outlook add-ins is not allowed,E3,L2,9.4,Restrict Unnecessary or Unauthorized Browser and Email Client Extensions,FALSE,TRUE,TRUE,TRUE,EXO
26,Test-NoAnonymousMeetingStart.ps1,8.5.2,E3,L1,FALSE,FALSE,FALSE 26,Test-ModernAuthExchangeOnline.ps1,6.5.1,Ensure modern authentication for Exchange Online is enabled (Automated),E3,L1,3.1,Encrypt Sensitive Data in Transit,FALSE,TRUE,TRUE,TRUE,EXO
27,Test-NotifyMalwareInternal.ps1,2.1.3,E3,L1,FALSE,TRUE,TRUE 27,Test-MailTipsEnabled.ps1,6.5.2,Ensure MailTips are enabled for end users,E3,L2,0,Explicitly Not Mapped,FALSE,FALSE,FALSE,TRUE,EXO
28,Test-NoWhitelistDomains.ps1,6.2.2,E3,L1,FALSE,FALSE,FALSE 28,Test-RestrictStorageProvidersOutlook.ps1,6.5.3,Ensure additional storage providers are restricted in Outlook on the web,E3,L2,3.3,Configure Data Access Control Lists,TRUE,TRUE,TRUE,TRUE,EXO
29,Test-OneDriveContentRestrictions.ps1,7.2.4,E3,L2,TRUE,TRUE,TRUE 29,Test-ModernAuthSharePoint.ps1,7.2.1,Modern Authentication for SharePoint Applications,E3,L1,3.1,Encrypt Sensitive Data in Transit,FALSE,TRUE,TRUE,TRUE,SPO
30,Test-OneDriveSyncRestrictions.ps1,7.3.2,E3,L2,FALSE,FALSE,FALSE 30,Test-SharePointAADB2B.ps1,7.2.2,Ensure SharePoint and OneDrive integration with Azure AD B2B is enabled,E3,L1,0,Explicitly Not Mapped,FALSE,FALSE,FALSE,TRUE,SPO
31,Test-OrganizersPresent.ps1,8.5.6,E3,L1,FALSE,FALSE,FALSE 31,Test-RestrictExternalSharing.ps1,7.2.3,Ensure external content sharing is restricted,E3,L1,3.3,Configure Data Access Control Lists,TRUE,TRUE,TRUE,TRUE,SPO
32,Test-OrgOnlyBypassLobby.ps1,8.5.3,E3,L1,FALSE,FALSE,TRUE 32,Test-OneDriveContentRestrictions.ps1,7.2.4,Ensure OneDrive content sharing is restricted,E3,L2,3.3,Configure Data Access Control Lists,TRUE,TRUE,TRUE,TRUE,SPO
33,Test-PasswordHashSync.ps1,5.1.8.1,E3,L1,FALSE,TRUE,TRUE 33,Test-SharePointGuestsItemSharing.ps1,7.2.5,Ensure that SharePoint guest users cannot share items they don't own,E3,L2,3.3,Configure Data Access Control Lists,TRUE,TRUE,TRUE,TRUE,SPO
34,Test-PasswordNeverExpirePolicy.ps1,1.3.1,E3,L1,TRUE,TRUE,TRUE 34,Test-SharePointExternalSharingDomains.ps1,7.2.6,Ensure SharePoint external sharing is managed through domain whitelist/blacklists,E3,L2,3.3,Configure Data Access Control Lists,TRUE,TRUE,TRUE,TRUE,SPO
35,Test-ReauthWithCode.ps1,7.2.10,E3,L1,FALSE,FALSE,FALSE 35,Test-LinkSharingRestrictions.ps1,7.2.7,Ensure link sharing is restricted in SharePoint and OneDrive,E3,L1,3.3,Configure Data Access Control Lists,TRUE,TRUE,TRUE,TRUE,SPO
36,Test-ReportSecurityInTeams.ps1,8.6.1,E3,L1,FALSE,FALSE,FALSE 36,Test-GuestAccessExpiration.ps1,7.2.9,Ensure guest access to a site or OneDrive will expire automatically,E3,L1,0,Explicitly Not Mapped,FALSE,FALSE,FALSE,TRUE,SPO
37,Test-RestrictCustomScripts.ps1,7.3.4,E3,L1,FALSE,FALSE,TRUE 37,Test-ReauthWithCode.ps1,7.2.10,Ensure reauthentication with verification code is restricted,E3,L1,0,Explicitly Not Mapped,FALSE,FALSE,FALSE,TRUE,SPO
38,Test-RestrictExternalSharing.ps1,7.2.3,E3,L1,TRUE,TRUE,TRUE 38,Test-DisallowInfectedFilesDownload.ps1,7.3.1,Ensure Office 365 SharePoint infected files are disallowed for download,E5,L2,10.1,Deploy and Maintain Anti-Malware Software,TRUE,TRUE,TRUE,TRUE,SPO
39,Test-RestrictOutlookAddins.ps1,6.3.1,E3,L2,FALSE,TRUE,TRUE 39,Test-OneDriveSyncRestrictions.ps1,7.3.2,Ensure OneDrive sync is restricted for unmanaged devices,E3,L2,0,Explicitly Not Mapped,FALSE,FALSE,FALSE,TRUE,SPO
40,Test-RestrictStorageProvidersOutlook.ps1,6.5.3,E3,L2,TRUE,TRUE,TRUE 40,Test-RestrictCustomScripts.ps1,7.3.4,Ensure custom script execution is restricted on site collections,E3,L1,2.7,Allowlist Authorized Scripts,FALSE,FALSE,TRUE,TRUE,SPO
41,Test-RestrictTenantCreation.ps1,5.1.2.3,E3,L1,FALSE,FALSE,FALSE 41,Test-TeamsExternalFileSharing.ps1,8.1.1,Ensure external file sharing in Teams is enabled for only approved cloud storage services,E3,L2,3.3,Configure Data Access Control Lists,TRUE,TRUE,TRUE,TRUE,Microsoft Teams
42,Test-SafeAttachmentsPolicy.ps1,2.1.4,E5,L2,FALSE,FALSE,TRUE 42,Test-BlockChannelEmails.ps1,8.1.2,Ensure users can't send emails to a channel email address,E3,L1,0,Explicitly Not Mapped,FALSE,FALSE,FALSE,TRUE,Microsoft Teams
43,Test-SafeAttachmentsTeams.ps1,2.1.5,E5,L2,TRUE,TRUE,TRUE 43,Test-TeamsExternalAccess.ps1,8.2.1,Ensure 'external access' is restricted in the Teams admin center,E3,L2,0,Explicitly Not Mapped,FALSE,FALSE,FALSE,TRUE,Microsoft Teams
44,Test-SafeLinksOfficeApps.ps1,2.1.1,E5,L2,TRUE,TRUE,TRUE 44,Test-NoAnonymousMeetingJoin.ps1,8.5.1,Ensure anonymous users can't join a meeting,E3,L2,0,Explicitly Not Mapped,FALSE,FALSE,FALSE,TRUE,Microsoft Teams
45,Test-SharePointAADB2B.ps1,7.2.2,E3,L1,FALSE,FALSE,FALSE 45,Test-NoAnonymousMeetingStart.ps1,8.5.2,Ensure anonymous users and dial-in callers can't start a meeting,E3,L1,0,Explicitly Not Mapped,FALSE,FALSE,FALSE,TRUE,Microsoft Teams
46,Test-SharePointExternalSharingDomains.ps1,7.2.6,E3,L2,TRUE,TRUE,TRUE 46,Test-OrgOnlyBypassLobby.ps1,8.5.3,Ensure only people in my org can bypass the lobby,E3,L1,6.8,Define and Maintain Role-Based Access Control,FALSE,FALSE,TRUE,TRUE,Microsoft Teams
47,Test-SharePointGuestsItemSharing.ps1,7.2.5,E3,L2,TRUE,TRUE,TRUE 47,Test-DialInBypassLobby.ps1,8.5.4,Ensure users dialing in can't bypass the lobby,E3,L1,0,Explicitly Not Mapped,FALSE,FALSE,FALSE,TRUE,Microsoft Teams
48,Test-SpamPolicyAdminNotify.ps1,2.1.6,E3,L1,FALSE,TRUE,TRUE 48,Test-MeetingChatNoAnonymous.ps1,8.5.5,Ensure meeting chat does not allow anonymous users,E3,L1,0,Explicitly Not Mapped,FALSE,FALSE,FALSE,TRUE,Microsoft Teams
49,Test-TeamsExternalAccess.ps1,8.2.1,E3,L2,FALSE,FALSE,FALSE 49,Test-OrganizersPresent.ps1,8.5.6,Ensure only organizers and co-organizers can present,E3,L1,0,Explicitly Not Mapped,FALSE,FALSE,FALSE,TRUE,Microsoft Teams
50,Test-TeamsExternalFileSharing.ps1,8.1.1,E3,L2,TRUE,TRUE,TRUE 50,Test-ExternalNoControl.ps1,8.5.7,Ensure external participants can't give or request control,E3,L1,0,Explicitly Not Mapped,FALSE,FALSE,FALSE,TRUE,Microsoft Teams
51,Test-ReportSecurityInTeams.ps1,8.6.1,Ensure users can report security concerns in Teams,E3,L1,0,Explicitly Not Mapped,FALSE,FALSE,FALSE,TRUE,Microsoft Teams | EXO
1 Index TestFileName Rec RecDescription ELevel ProfileLevel CISControl CISDescription IG1 IG2 IG3 Automated Connection
2 1 Test-AntiPhishingPolicy.ps1 Test-AdministrativeAccountCompliance.ps1 2.1.7 1.1.1 Ensure Administrative accounts are separate and cloud-only E5 E3 L1 5.4 Restrict Administrator Privileges to Dedicated Administrator Accounts FALSE TRUE FALSE TRUE TRUE FALSE Microsoft Graph
3 2 Test-AuditDisabledFalse.ps1 Test-GlobalAdminsCount.ps1 6.1.1 1.1.3 Ensure that between two and four global admins are designated E3 L1 5.1 Establish and Maintain an Inventory of Accounts TRUE TRUE TRUE TRUE Microsoft Graph
4 3 Test-AuditLogSearch.ps1 Test-ManagedApprovedPublicGroups.ps1 3.1.1 1.2.1 Ensure that only organizationally managed/approved public groups exist E3 L1 L2 3.3 Configure Data Access Control Lists TRUE TRUE TRUE TRUE Microsoft Graph
5 4 Test-BlockChannelEmails.ps1 Test-BlockSharedMailboxSignIn.ps1 8.1.2 1.2.2 Ensure sign-in to shared mailboxes is blocked E3 L1 0 Explicitly Not Mapped FALSE FALSE FALSE TRUE AzureAD | EXO
6 5 Test-BlockMailForwarding.ps1 Test-PasswordNeverExpirePolicy.ps1 6.2.1 1.3.1 Ensure the 'Password expiration policy' is set to 'Set passwords to never expire' E3 L1 5.2 Use Unique Passwords FALSE TRUE FALSE TRUE FALSE TRUE TRUE Microsoft Graph
7 6 Test-BlockSharedMailboxSignIn.ps1 Test-ExternalSharingCalendars.ps1 1.2.2 1.3.3 Ensure 'External sharing' of calendars is not available E3 L1 L2 4.8 Uninstall or Disable Unnecessary Services on Enterprise Assets and Software FALSE FALSE TRUE FALSE TRUE TRUE EXO
8 7 Test-CommonAttachmentFilter.ps1 Test-CustomerLockbox.ps1 2.1.2 1.3.6 Ensure the customer lockbox feature is enabled E3 E5 L1 L2 0 Explicitly Not Mapped FALSE TRUE FALSE TRUE FALSE TRUE EXO
9 8 Test-CustomerLockbox.ps1 Test-SafeLinksOfficeApps.ps1 1.3.6 2.1.1 Ensure Safe Links for Office Applications is Enabled E5 L2 10.1 Deploy and Maintain Anti-Malware Software FALSE TRUE FALSE TRUE FALSE TRUE TRUE EXO
10 9 Test-DialInBypassLobby.ps1 Test-CommonAttachmentFilter.ps1 8.5.4 2.1.2 Ensure the Common Attachment Types Filter is enabled E3 L1 9.6 Block Unnecessary File Types FALSE FALSE TRUE FALSE TRUE TRUE EXO
11 10 Test-DisallowInfectedFilesDownload.ps1 Test-NotifyMalwareInternal.ps1 7.3.1 2.1.3 Ensure notifications for internal users sending malware is Enabled E5 E3 L2 L1 17.5 Assign Key Roles and Responsibilities TRUE FALSE TRUE TRUE TRUE EXO
12 11 Test-EnableDKIM.ps1 Test-SafeAttachmentsPolicy.ps1 2.1.9 2.1.4 Ensure Safe Attachments policy is enabled E3 E5 L1 L2 9.7 Deploy and Maintain Email Server Anti-Malware Protections FALSE TRUE FALSE TRUE TRUE EXO
13 12 Test-ExternalNoControl.ps1 Test-SafeAttachmentsTeams.ps1 8.5.7 2.1.5 Ensure Safe Attachments for SharePoint, OneDrive, and Microsoft Teams is Enabled E3 E5 L1 L2 9.7,10.1 Deploy and Maintain Email Server Anti-Malware Protections, Deploy and Maintain Anti-Malware Software FALSE TRUE FALSE TRUE FALSE TRUE TRUE EXO
14 13 Test-ExternalSharingCalendars.ps1 Test-SpamPolicyAdminNotify.ps1 1.3.3 2.1.6 Ensure Exchange Online Spam Policies are set to notify administrators E3 L2 L1 17.5 Assign Key Roles and Responsibilities FALSE TRUE TRUE TRUE EXO
15 14 Test-GlobalAdminsCount.ps1 Test-AntiPhishingPolicy.ps1 1.1.3 2.1.7 Ensure that an anti-phishing policy has been created E3 E5 L1 9.7 Deploy and Maintain Email Server Anti-Malware Protections TRUE FALSE TRUE FALSE TRUE TRUE EXO
16 15 Test-GuestAccessExpiration.ps1 Test-EnableDKIM.ps1 7.2.9 2.1.9 Ensure that DKIM is enabled for all Exchange Online Domains E3 L1 9.5 Implement DMARC FALSE FALSE TRUE FALSE TRUE TRUE EXO
17 16 Test-IdentifyExternalEmail.ps1 Test-AuditLogSearch.ps1 6.2.3 3.1.1 Ensure Microsoft 365 audit log search is Enabled E3 L1 8.2 Collect Audit Logs FALSE TRUE FALSE TRUE FALSE TRUE TRUE EXO
18 17 Test-LinkSharingRestrictions.ps1 Test-RestrictTenantCreation.ps1 7.2.7 5.1.2.3 Ensure 'Restrict non-admin users from creating tenants' is set to 'Yes' E3 L1 0 Explicitly Not Mapped TRUE FALSE TRUE FALSE TRUE FALSE TRUE Microsoft Graph
19 18 Test-MailboxAuditingE3.ps1 Test-PasswordHashSync.ps1 6.1.2 5.1.8.1 Ensure password hash sync is enabled for hybrid deployments E3 L1 6.7 Centralize Access Control TRUE FALSE TRUE TRUE TRUE Microsoft Graph
20 19 Test-MailboxAuditingE5.ps1 Test-AuditDisabledFalse.ps1 6.1.3 6.1.1 Ensure 'AuditDisabled' organizationally is set to 'False' E5 E3 L1 8.2 Collect Audit Logs TRUE TRUE TRUE TRUE Microsoft Graph
21 20 Test-MailTipsEnabled.ps1 Test-MailboxAuditingE3.ps1 6.5.2 6.1.2 Ensure mailbox auditing for Office E3 users is Enabled E3 L2 L1 8.2 Collect audit logs. FALSE TRUE FALSE TRUE FALSE TRUE TRUE AzureAD | EXO | Microsoft Graph
22 21 Test-ManagedApprovedPublicGroups.ps1 Test-MailboxAuditingE5.ps1 1.2.1 6.1.3 Ensure mailbox auditing for Office E5 users is Enabled E3 E5 L2 L1 8.2 Collect audit logs. TRUE TRUE TRUE TRUE AzureAD | EXO | Microsoft Graph
23 22 Test-MeetingChatNoAnonymous.ps1 Test-BlockMailForwarding.ps1 8.5.5 6.2.1 Ensure all forms of mail forwarding are blocked and/or disabled E3 L1 0 Explicitly Not Mapped FALSE FALSE FALSE TRUE EXO
24 23 Test-ModernAuthExchangeOnline.ps1 Test-NoWhitelistDomains.ps1 6.5.1 6.2.2 Ensure mail transport rules do not whitelist specific domains E3 L1 0 Explicitly Not Mapped FALSE TRUE FALSE TRUE FALSE TRUE EXO
25 24 Test-ModernAuthSharePoint.ps1 Test-IdentifyExternalEmail.ps1 7.2.1 6.2.3 Ensure email from external senders is identified E3 L1 0 Explicitly Not Mapped FALSE TRUE FALSE TRUE FALSE TRUE EXO
26 25 Test-NoAnonymousMeetingJoin.ps1 Test-RestrictOutlookAddins.ps1 8.5.1 6.3.1 Ensure users installing Outlook add-ins is not allowed E3 L2 9.4 Restrict Unnecessary or Unauthorized Browser and Email Client Extensions FALSE FALSE TRUE FALSE TRUE TRUE EXO
27 26 Test-NoAnonymousMeetingStart.ps1 Test-ModernAuthExchangeOnline.ps1 8.5.2 6.5.1 Ensure modern authentication for Exchange Online is enabled (Automated) E3 L1 3.1 Encrypt Sensitive Data in Transit FALSE FALSE TRUE FALSE TRUE TRUE EXO
28 27 Test-NotifyMalwareInternal.ps1 Test-MailTipsEnabled.ps1 2.1.3 6.5.2 Ensure MailTips are enabled for end users E3 L1 L2 0 Explicitly Not Mapped FALSE TRUE FALSE TRUE FALSE TRUE EXO
29 28 Test-NoWhitelistDomains.ps1 Test-RestrictStorageProvidersOutlook.ps1 6.2.2 6.5.3 Ensure additional storage providers are restricted in Outlook on the web E3 L1 L2 3.3 Configure Data Access Control Lists FALSE TRUE FALSE TRUE FALSE TRUE TRUE EXO
30 29 Test-OneDriveContentRestrictions.ps1 Test-ModernAuthSharePoint.ps1 7.2.4 7.2.1 Modern Authentication for SharePoint Applications E3 L2 L1 3.1 Encrypt Sensitive Data in Transit TRUE FALSE TRUE TRUE TRUE SPO
31 30 Test-OneDriveSyncRestrictions.ps1 Test-SharePointAADB2B.ps1 7.3.2 7.2.2 Ensure SharePoint and OneDrive integration with Azure AD B2B is enabled E3 L2 L1 0 Explicitly Not Mapped FALSE FALSE FALSE TRUE SPO
32 31 Test-OrganizersPresent.ps1 Test-RestrictExternalSharing.ps1 8.5.6 7.2.3 Ensure external content sharing is restricted E3 L1 3.3 Configure Data Access Control Lists FALSE TRUE FALSE TRUE FALSE TRUE TRUE SPO
33 32 Test-OrgOnlyBypassLobby.ps1 Test-OneDriveContentRestrictions.ps1 8.5.3 7.2.4 Ensure OneDrive content sharing is restricted E3 L1 L2 3.3 Configure Data Access Control Lists FALSE TRUE FALSE TRUE TRUE TRUE SPO
34 33 Test-PasswordHashSync.ps1 Test-SharePointGuestsItemSharing.ps1 5.1.8.1 7.2.5 Ensure that SharePoint guest users cannot share items they don't own E3 L1 L2 3.3 Configure Data Access Control Lists FALSE TRUE TRUE TRUE TRUE SPO
35 34 Test-PasswordNeverExpirePolicy.ps1 Test-SharePointExternalSharingDomains.ps1 1.3.1 7.2.6 Ensure SharePoint external sharing is managed through domain whitelist/blacklists E3 L1 L2 3.3 Configure Data Access Control Lists TRUE TRUE TRUE TRUE SPO
36 35 Test-ReauthWithCode.ps1 Test-LinkSharingRestrictions.ps1 7.2.10 7.2.7 Ensure link sharing is restricted in SharePoint and OneDrive E3 L1 3.3 Configure Data Access Control Lists FALSE TRUE FALSE TRUE FALSE TRUE TRUE SPO
37 36 Test-ReportSecurityInTeams.ps1 Test-GuestAccessExpiration.ps1 8.6.1 7.2.9 Ensure guest access to a site or OneDrive will expire automatically E3 L1 0 Explicitly Not Mapped FALSE FALSE FALSE TRUE SPO
38 37 Test-RestrictCustomScripts.ps1 Test-ReauthWithCode.ps1 7.3.4 7.2.10 Ensure reauthentication with verification code is restricted E3 L1 0 Explicitly Not Mapped FALSE FALSE TRUE FALSE TRUE SPO
39 38 Test-RestrictExternalSharing.ps1 Test-DisallowInfectedFilesDownload.ps1 7.2.3 7.3.1 Ensure Office 365 SharePoint infected files are disallowed for download E3 E5 L1 L2 10.1 Deploy and Maintain Anti-Malware Software TRUE TRUE TRUE TRUE SPO
40 39 Test-RestrictOutlookAddins.ps1 Test-OneDriveSyncRestrictions.ps1 6.3.1 7.3.2 Ensure OneDrive sync is restricted for unmanaged devices E3 L2 0 Explicitly Not Mapped FALSE TRUE FALSE TRUE FALSE TRUE SPO
41 40 Test-RestrictStorageProvidersOutlook.ps1 Test-RestrictCustomScripts.ps1 6.5.3 7.3.4 Ensure custom script execution is restricted on site collections E3 L2 L1 2.7 Allowlist Authorized Scripts TRUE FALSE TRUE FALSE TRUE TRUE SPO
42 41 Test-RestrictTenantCreation.ps1 Test-TeamsExternalFileSharing.ps1 5.1.2.3 8.1.1 Ensure external file sharing in Teams is enabled for only approved cloud storage services E3 L1 L2 3.3 Configure Data Access Control Lists FALSE TRUE FALSE TRUE FALSE TRUE TRUE Microsoft Teams
43 42 Test-SafeAttachmentsPolicy.ps1 Test-BlockChannelEmails.ps1 2.1.4 8.1.2 Ensure users can't send emails to a channel email address E5 E3 L2 L1 0 Explicitly Not Mapped FALSE FALSE TRUE FALSE TRUE Microsoft Teams
44 43 Test-SafeAttachmentsTeams.ps1 Test-TeamsExternalAccess.ps1 2.1.5 8.2.1 Ensure 'external access' is restricted in the Teams admin center E5 E3 L2 0 Explicitly Not Mapped TRUE FALSE TRUE FALSE TRUE FALSE TRUE Microsoft Teams
45 44 Test-SafeLinksOfficeApps.ps1 Test-NoAnonymousMeetingJoin.ps1 2.1.1 8.5.1 Ensure anonymous users can't join a meeting E5 E3 L2 0 Explicitly Not Mapped TRUE FALSE TRUE FALSE TRUE FALSE TRUE Microsoft Teams
46 45 Test-SharePointAADB2B.ps1 Test-NoAnonymousMeetingStart.ps1 7.2.2 8.5.2 Ensure anonymous users and dial-in callers can't start a meeting E3 L1 0 Explicitly Not Mapped FALSE FALSE FALSE TRUE Microsoft Teams
47 46 Test-SharePointExternalSharingDomains.ps1 Test-OrgOnlyBypassLobby.ps1 7.2.6 8.5.3 Ensure only people in my org can bypass the lobby E3 L2 L1 6.8 Define and Maintain Role-Based Access Control TRUE FALSE TRUE FALSE TRUE TRUE Microsoft Teams
48 47 Test-SharePointGuestsItemSharing.ps1 Test-DialInBypassLobby.ps1 7.2.5 8.5.4 Ensure users dialing in can't bypass the lobby E3 L2 L1 0 Explicitly Not Mapped TRUE FALSE TRUE FALSE TRUE FALSE TRUE Microsoft Teams
49 48 Test-SpamPolicyAdminNotify.ps1 Test-MeetingChatNoAnonymous.ps1 2.1.6 8.5.5 Ensure meeting chat does not allow anonymous users E3 L1 0 Explicitly Not Mapped FALSE TRUE FALSE TRUE FALSE TRUE Microsoft Teams
50 49 Test-TeamsExternalAccess.ps1 Test-OrganizersPresent.ps1 8.2.1 8.5.6 Ensure only organizers and co-organizers can present E3 L2 L1 0 Explicitly Not Mapped FALSE FALSE FALSE TRUE Microsoft Teams
51 50 Test-TeamsExternalFileSharing.ps1 Test-ExternalNoControl.ps1 8.1.1 8.5.7 Ensure external participants can't give or request control E3 L2 L1 0 Explicitly Not Mapped TRUE FALSE TRUE FALSE TRUE FALSE TRUE Microsoft Teams
52 51 Test-ReportSecurityInTeams.ps1 8.6.1 Ensure users can report security concerns in Teams E3 L1 0 Explicitly Not Mapped FALSE FALSE FALSE TRUE Microsoft Teams | EXO

View File

@@ -0,0 +1,136 @@
function Test-AdministrativeAccountCompliance {
[CmdletBinding()]
param (
# Aligned
# Parameters can be added if needed
)
begin {
# The following conditions are checked:
# Condition A: The administrative account is cloud-only (not synced).
# Condition B: The account is assigned a valid license (e.g., Microsoft Entra ID P1 or P2).
# Condition C: The administrative account does not have any other application assignments (only valid licenses).
$validLicenses = @('AAD_PREMIUM', 'AAD_PREMIUM_P2')
$recnum = "1.1.1"
Write-Verbose "Starting Test-AdministrativeAccountCompliance with Rec: $recnum"
}
process {
try {
# Retrieve all admin roles
Write-Verbose "Retrieving all admin roles"
$adminRoles = Get-MgRoleManagementDirectoryRoleDefinition | Where-Object { $_.DisplayName -like "*Admin*" }
$adminRoleUsers = @()
# Loop through each admin role to get role assignments and user details
foreach ($role in $adminRoles) {
Write-Verbose "Processing role: $($role.DisplayName)"
$roleAssignments = Get-MgRoleManagementDirectoryRoleAssignment -Filter "roleDefinitionId eq '$($role.Id)'"
foreach ($assignment in $roleAssignments) {
Write-Verbose "Processing role assignment for principal ID: $($assignment.PrincipalId)"
# Get user details for each principal ID
$userDetails = Get-MgUser -UserId $assignment.PrincipalId -Property "DisplayName, UserPrincipalName, Id, OnPremisesSyncEnabled" -ErrorAction SilentlyContinue
if ($userDetails) {
Write-Verbose "Retrieved user details for: $($userDetails.UserPrincipalName)"
# Get user license details
$licenses = Get-MgUserLicenseDetail -UserId $assignment.PrincipalId -ErrorAction SilentlyContinue
$licenseString = if ($licenses) { ($licenses.SkuPartNumber -join '|') } else { "No Licenses Found" }
# Condition A: Check if the account is cloud-only
$cloudOnlyStatus = if ($userDetails.OnPremisesSyncEnabled) { "Fail" } else { "Pass" }
# Condition B: Check if the account has valid licenses
$hasValidLicense = $licenses.SkuPartNumber | ForEach-Object { $validLicenses -contains $_ }
$validLicensesStatus = if ($hasValidLicense) { "Pass" } else { "Fail" }
# Condition C: Check if the account has no other licenses
$hasInvalidLicense = $licenses.SkuPartNumber | ForEach-Object { $validLicenses -notcontains $_ }
$applicationAssignmentStatus = if ($hasInvalidLicense) { "Fail" } else { "Pass" }
Write-Verbose "User: $($userDetails.UserPrincipalName), Cloud-Only: $cloudOnlyStatus, Valid Licenses: $validLicensesStatus, Other Applications Assigned: $applicationAssignmentStatus"
# Collect user information
$adminRoleUsers += [PSCustomObject]@{
UserName = $userDetails.UserPrincipalName
RoleName = $role.DisplayName
UserId = $userDetails.Id
HybridUser = $userDetails.OnPremisesSyncEnabled
Licenses = $licenseString
CloudOnlyStatus = $cloudOnlyStatus
ValidLicensesStatus = $validLicensesStatus
ApplicationAssignmentStatus = $applicationAssignmentStatus
}
}
else {
Write-Verbose "No user details found for principal ID: $($assignment.PrincipalId)"
}
}
}
# Group admin role users by UserName and collect unique roles and licenses
Write-Verbose "Grouping admin role users by UserName"
$uniqueAdminRoleUsers = $adminRoleUsers | Group-Object -Property UserName | ForEach-Object {
$first = $_.Group | Select-Object -First 1
$roles = ($_.Group.RoleName -join ', ')
$licenses = (($_.Group | Select-Object -ExpandProperty Licenses) -join ',').Split(',') | Select-Object -Unique
$first | Select-Object UserName, UserId, HybridUser, @{Name = 'Roles'; Expression = { $roles } }, @{Name = 'Licenses'; Expression = { $licenses -join '|' } }, CloudOnlyStatus, ValidLicensesStatus, ApplicationAssignmentStatus
}
# Identify non-compliant users based on conditions A, B, and C
Write-Verbose "Identifying non-compliant users based on conditions"
$nonCompliantUsers = $uniqueAdminRoleUsers | Where-Object {
$_.HybridUser -or # Fails Condition A
$_.ValidLicensesStatus -eq "Fail" -or # Fails Condition B
$_.ApplicationAssignmentStatus -eq "Fail" # Fails Condition C
}
# Generate failure reasons
Write-Verbose "Generating failure reasons for non-compliant users"
$failureReasons = $nonCompliantUsers | ForEach-Object {
"$($_.UserName)|$($_.Roles)|$($_.CloudOnlyStatus)|$($_.ValidLicensesStatus)|$($_.ApplicationAssignmentStatus)"
}
$failureReasons = $failureReasons -join "`n"
$failureReason = if ($nonCompliantUsers) {
"Non-Compliant Accounts: $($nonCompliantUsers.Count)"
} else {
"Compliant Accounts: $($uniqueAdminRoleUsers.Count)"
}
$result = $nonCompliantUsers.Count -eq 0
$status = if ($result) { 'Pass' } else { 'Fail' }
$details = if ($nonCompliantUsers) { "Non-compliant accounts: `nUsername | Roles | Cloud-Only Status | Entra ID License Status | Other Applications Assigned Status`n$failureReasons" } else { "N/A" }
Write-Verbose "Assessment completed. Result: $status"
# Create the parameter splat
$params = @{
Rec = $recnum
Result = $result
Status = $status
Details = $details
FailureReason = $failureReason
}
$auditResult = Initialize-CISAuditResult @params
}
catch {
Write-Error "An error occurred during the test: $_"
# Handle the error and create a failure result
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
}
end {
# Output the result
return $auditResult
}
}

View File

@@ -1,59 +1,112 @@
function Test-AntiPhishingPolicy { function Test-AntiPhishingPolicy {
[CmdletBinding()] [CmdletBinding()]
[OutputType([CISAuditResult])]
param ( param (
# Aligned
# Parameters can be added if needed # Parameters can be added if needed
) )
begin { begin {
# Dot source the class script # Dot source the class script if necessary
#. .\source\Classes\CISAuditResult.ps1
# Initialization code, if needed
#$auditResults = @()
$recnum = "2.1.7"
$auditResults = @() <#
Conditions for 2.1.7 (L1) Ensure that an anti-phishing policy has been created
Validate test for a pass:
- Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
- Specific conditions to check:
- Condition A: Verify that an anti-phishing policy exists in the Microsoft 365 Security Center.
- Condition B: Using PowerShell, ensure the anti-phishing policy is configured with appropriate settings such as enabling impersonation protection and spoof intelligence.
Validate test for a fail:
- Confirm that the failure conditions in the automated test are consistent with the manual audit results.
- Specific conditions to check:
- Condition A: No anti-phishing policy exists in the Microsoft 365 Security Center.
- Condition B: Using PowerShell, the anti-phishing policy is not configured with the required settings.
#>
} }
process { process {
# Retrieve and validate the anti-phishing policies try {
$antiPhishPolicies = Get-AntiPhishPolicy # Condition A: Ensure that an anti-phishing policy has been created
$validatedPolicies = $antiPhishPolicies | Where-Object { $antiPhishPolicies = Get-AntiPhishPolicy
$_.Enabled -eq $true -and
$_.PhishThresholdLevel -ge 2 -and # Condition B: Verify the anti-phishing policy settings using PowerShell
$_.EnableMailboxIntelligenceProtection -eq $true -and $validatedPolicies = $antiPhishPolicies | Where-Object {
$_.EnableMailboxIntelligence -eq $true -and $_.Enabled -eq $true -and
$_.EnableSpoofIntelligence -eq $true $_.PhishThresholdLevel -ge 2 -and
$_.EnableMailboxIntelligenceProtection -eq $true -and
$_.EnableMailboxIntelligence -eq $true -and
$_.EnableSpoofIntelligence -eq $true
}
# Check if there is at least one policy that meets the requirements
$nonCompliantItems = $antiPhishPolicies | Where-Object {
$_.Enabled -ne $true -or
$_.PhishThresholdLevel -lt 2 -or
$_.EnableMailboxIntelligenceProtection -ne $true -or
$_.EnableMailboxIntelligence -ne $true -or
$_.EnableSpoofIntelligence -ne $true
}
$compliantItems = $validatedPolicies
$isCompliant = $compliantItems.Count -gt 0
# Prepare failure reasons for non-compliant items
$nonCompliantNames = $nonCompliantItems | ForEach-Object { $_.Name }
$failureReasons = if ($nonCompliantNames.Count -gt 0) {
"Reason: Does not meet one or more compliance criteria.`nNon-compliant Policies:`n" + ($nonCompliantNames -join "`n")
}
else {
"N/A"
}
# Prepare details for non-compliant items
$nonCompliantDetails = $nonCompliantItems | ForEach-Object {
"Policy: $($_.Name)"
}
$nonCompliantDetails = $nonCompliantDetails -join "`n"
# Prepare details based on compliance
$details = if ($nonCompliantItems) {
"Non-Compliant Items: $($nonCompliantItems.Count)`nDetails:`n$nonCompliantDetails"
}
else {
"Compliant Items: $($compliantItems.Count)"
}
# Parameter splat for Initialize-CISAuditResult function
$params = @{
Rec = $recnum
Result = $nonCompliantItems.Count -eq 0
Status = if ($isCompliant) { "Pass" } else { "Fail" }
Details = $details
FailureReason = $failureReasons
}
# Create and populate the CISAuditResult object
$auditResult = Initialize-CISAuditResult @params
} }
catch {
Write-Error "An error occurred during the test: $_"
# Check if there is at least one policy that meets the requirements # Retrieve the description from the test definitions
$isCompliant = $validatedPolicies.Count -gt 0 $testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
# Prepare failure details if policies are not compliant $script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
$failureDetails = if (-not $isCompliant) {
"No anti-phishing policy is fully compliant with CIS benchmark requirements." # Call Initialize-CISAuditResult with error parameters
} else { $auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
"Compliant Anti-Phish Policy Names: " + ($validatedPolicies.Name -join ', ')
} }
# Create an instance of CISAuditResult and populate it
$auditResult = [CISAuditResult]::new()
$auditResult.Status = if ($isCompliant) { "Pass" } else { "Fail" }
$auditResult.ELevel = "E5"
$auditResult.ProfileLevel = "L1"
$auditResult.Rec = "2.1.7"
$auditResult.RecDescription = "Ensure that an anti-phishing policy has been created"
$auditResult.CISControlVer = "v8"
$auditResult.CISControl = "9.7"
$auditResult.CISDescription = "Deploy and Maintain Email Server Anti-Malware Protections"
$auditResult.IG1 = $false
$auditResult.IG2 = $false
$auditResult.IG3 = $true
$auditResult.Result = $isCompliant
$auditResult.Details = $failureDetails
$auditResult.FailureReason = if (-not $isCompliant) { "Anti-phishing policies do not meet CIS benchmark requirements." } else { "N/A" }
$auditResults += $auditResult
} }
end { end {
# Return auditResults # Return auditResult
return $auditResults return $auditResult
} }
} }

View File

@@ -1,43 +1,84 @@
function Test-AuditDisabledFalse { function Test-AuditDisabledFalse {
[CmdletBinding()] [CmdletBinding()]
[OutputType([CISAuditResult])]
# Aligned
param ( param (
# Parameters can be added if needed # Parameters can be added if needed
) )
begin { begin {
# Dot source the class script # Dot source the class script if necessary
#. .\source\Classes\CISAuditResult.ps1
$auditResults = @() # Conditions for 6.1.1 (L1) Ensure 'AuditDisabled' organizationally is set to 'False'
#
# Validate test for a pass:
# - Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
# - Specific conditions to check:
# - Condition A: The `AuditDisabled` organizational setting is set to `False` in the Microsoft 365 admin center.
# - Condition B: Using PowerShell, the `AuditDisabled` property in the organization's configuration is set to `False`.
# - Condition C: Ensure mailbox auditing is enabled by default at the organizational level.
#
# Validate test for a fail:
# - Confirm that the failure conditions in the automated test are consistent with the manual audit results.
# - Specific conditions to check:
# - Condition A: The `AuditDisabled` organizational setting is set to `True` in the Microsoft 365 admin center.
# - Condition B: Using PowerShell, the `AuditDisabled` property in the organization's configuration is set to `True`.
# - Condition C: Mailbox auditing is not enabled by default at the organizational level.
# Initialization code, if needed
$recnum = "6.1.1"
} }
process { process {
# 6.1.1 (L1) Ensure 'AuditDisabled' organizationally is set to 'False' try {
# Pass if AuditDisabled is False. Fail otherwise. # 6.1.1 (L1) Ensure 'AuditDisabled' organizationally is set to 'False'
$auditDisabledConfig = Get-OrganizationConfig | Select-Object AuditDisabled
$auditNotDisabled = -not $auditDisabledConfig.AuditDisabled
# Create an instance of CISAuditResult and populate it # Retrieve the AuditDisabled configuration (Condition B)
$auditResult = [CISAuditResult]::new() $auditDisabledConfig = Get-OrganizationConfig | Select-Object AuditDisabled
$auditResult.Status = if ($auditNotDisabled) { "Pass" } else { "Fail" } $auditNotDisabled = -not $auditDisabledConfig.AuditDisabled
$auditResult.ELevel = "E3"
$auditResult.ProfileLevel = "L1"
$auditResult.Rec = "6.1.1"
$auditResult.RecDescription = "Ensure 'AuditDisabled' organizationally is set to 'False'"
$auditResult.CISControlVer = "v8"
$auditResult.CISControl = "8.2"
$auditResult.CISDescription = "Collect Audit Logs"
$auditResult.IG1 = $true
$auditResult.IG2 = $true
$auditResult.IG3 = $true
$auditResult.Result = $auditNotDisabled
$auditResult.Details = if ($auditNotDisabled) { "Audit is not disabled organizationally" } else { "Audit is disabled organizationally" }
$auditResult.FailureReason = if (-not $auditNotDisabled) { "AuditDisabled is set to True" } else { "N/A" }
$auditResults += $auditResult # Prepare failure reasons and details based on compliance
$failureReasons = if (-not $auditNotDisabled) {
"AuditDisabled is set to True" # Condition A Fail
}
else {
"N/A"
}
$details = if ($auditNotDisabled) {
"Audit is not disabled organizationally" # Condition C Pass
}
else {
"Audit is disabled organizationally" # Condition C Fail
}
# Create and populate the CISAuditResult object
$params = @{
Rec = $recnum
Result = $auditNotDisabled
Status = if ($auditNotDisabled) { "Pass" } else { "Fail" }
Details = $details
FailureReason = $failureReasons
}
$auditResult = Initialize-CISAuditResult @params
}
catch {
Write-Error "An error occurred during the test: $_"
# Retrieve the description from the test definitions
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
} }
end { end {
# Return auditResults # Return the audit result
return $auditResults return $auditResult
} }
} }

View File

@@ -1,43 +1,88 @@
function Test-AuditLogSearch { function Test-AuditLogSearch {
[CmdletBinding()] [CmdletBinding()]
[OutputType([CISAuditResult])]
param ( param (
# Aligned
# Parameters can be added if needed # Parameters can be added if needed
) )
begin { begin {
# Dot source the class script # Dot source the class script if necessary
#. .\source\Classes\CISAuditResult.ps1
# Initialization code, if needed
$recnum = "3.1.1"
$auditResults = @() <#
Conditions for 3.1.1 (L1) Ensure Microsoft 365 audit log search is Enabled
Validate test for a pass:
- Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
- Specific conditions to check:
- Condition A: Audit log search is enabled in the Microsoft Purview compliance portal.
- Condition B: The audit log retains user and admin activity for 90 days.
- Condition C: Audit log search capabilities are functional (search results are displayed for activities within the past 30 days).
Validate test for a fail:
- Confirm that the failure conditions in the automated test are consistent with the manual audit results.
- Specific conditions to check:
- Condition A: Audit log search is not enabled in the Microsoft Purview compliance portal.
- Condition B: The audit log does not retain user and admin activity for 90 days.
- Condition C: Audit log search capabilities are non-functional (no search results are displayed for activities within the past 30 days).
#>
} }
process { process {
# 3.1.1 (L1) Ensure Microsoft 365 audit log search is Enabled
# Pass if UnifiedAuditLogIngestionEnabled is True. Fail otherwise.
$auditLogConfig = Get-AdminAuditLogConfig | Select-Object UnifiedAuditLogIngestionEnabled
$auditLogResult = $auditLogConfig.UnifiedAuditLogIngestionEnabled
# Create an instance of CISAuditResult and populate it try {
$auditResult = [CISAuditResult]::new() # 3.1.1 (L1) Ensure Microsoft 365 audit log search is Enabled
$auditResult.Status = if ($auditLogResult) { "Pass" } else { "Fail" }
$auditResult.ELevel = "E3"
$auditResult.ProfileLevel = "L1"
$auditResult.Rec = "3.1.1"
$auditResult.RecDescription = "Ensure Microsoft 365 audit log search is Enabled"
$auditResult.CISControlVer = "v8"
$auditResult.CISControl = "8.2"
$auditResult.CISDescription = "Collect Audit Logs"
$auditResult.IG1 = $true
$auditResult.IG2 = $true
$auditResult.IG3 = $true
$auditResult.Result = $auditLogResult
$auditResult.Details = "UnifiedAuditLogIngestionEnabled: $($auditLogConfig.UnifiedAuditLogIngestionEnabled)"
$auditResult.FailureReason = if (-not $auditLogResult) { "Audit log search is not enabled" } else { "N/A" }
$auditResults += $auditResult # Retrieve the audit log configuration
$auditLogConfig = Get-AdminAuditLogConfig | Select-Object UnifiedAuditLogIngestionEnabled
$auditLogResult = $auditLogConfig.UnifiedAuditLogIngestionEnabled
# Prepare failure reasons and details based on compliance
$failureReasons = if (-not $auditLogResult) {
# Condition A (Fail): Audit log search is not enabled in the Microsoft Purview compliance portal
"Audit log search is not enabled"
}
else {
"N/A"
}
$details = if ($auditLogResult) {
# Condition A (Pass): Audit log search is enabled in the Microsoft Purview compliance portal
"UnifiedAuditLogIngestionEnabled: True"
}
else {
"UnifiedAuditLogIngestionEnabled: False"
}
# Create and populate the CISAuditResult object
$params = @{
Rec = $recnum
Result = $auditLogResult
Status = if ($auditLogResult) { "Pass" } else { "Fail" }
Details = $details
FailureReason = $failureReasons
}
$auditResult = Initialize-CISAuditResult @params
}
catch {
Write-Error "An error occurred during the test: $_"
# Retrieve the description from the test definitions
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
} }
end { end {
# Return auditResults # Return the audit result
return $auditResults return $auditResult
} }
} }

View File

@@ -1,44 +1,82 @@
function Test-BlockChannelEmails { function Test-BlockChannelEmails {
[CmdletBinding()] [CmdletBinding()]
[OutputType([CISAuditResult])]
param ( param (
# Aligned
# Parameters can be added here if needed # Parameters can be added here if needed
) )
begin { begin {
# Dot source the class script # Dot source the class script if necessary
#. .\source\Classes\CISAuditResult.ps1
$auditResults = @() # Initialization code, if needed
$recnum = "8.1.2"
} }
process { process {
# 8.1.2 (L1) Ensure users can't send emails to a channel email address
# Connect to Teams PowerShell using Connect-MicrosoftTeams
$teamsClientConfig = Get-CsTeamsClientConfiguration -Identity Global try {
$allowEmailIntoChannel = $teamsClientConfig.AllowEmailIntoChannel # 8.1.2 (L1) Ensure users can't send emails to a channel email address
#
# Validate test for a pass:
# - Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
# - Specific conditions to check:
# - Condition A: The `AllowEmailIntoChannel` setting in Teams is set to `False`.
# - Condition B: The setting `Users can send emails to a channel email address` is set to `Off` in the Teams admin center.
# - Condition C: Verification using PowerShell confirms that the `AllowEmailIntoChannel` setting is disabled.
#
# Validate test for a fail:
# - Confirm that the failure conditions in the automated test are consistent with the manual audit results.
# - Specific conditions to check:
# - Condition A: The `AllowEmailIntoChannel` setting in Teams is not set to `False`.
# - Condition B: The setting `Users can send emails to a channel email address` is not set to `Off` in the Teams admin center.
# - Condition C: Verification using PowerShell indicates that the `AllowEmailIntoChannel` setting is enabled.
# Create an instance of CISAuditResult and populate it # Retrieve Teams client configuration
$auditResult = [CISAuditResult]::new() $teamsClientConfig = Get-CsTeamsClientConfiguration -Identity Global
$auditResult.CISControlVer = "v8" $allowEmailIntoChannel = $teamsClientConfig.AllowEmailIntoChannel
$auditResult.CISControl = "0.0" # This control is Explicitly Not Mapped as per the image provided
$auditResult.CISDescription = "Explicitly Not Mapped"
$auditResult.Rec = "8.1.2"
$auditResult.ELevel = "E3"
$auditResult.ProfileLevel = "L1"
$auditResult.IG1 = $false # Set based on the benchmark
$auditResult.IG2 = $false # Set based on the benchmark
$auditResult.IG3 = $false # Set based on the benchmark
$auditResult.RecDescription = "Ensure users can't send emails to a channel email address"
$auditResult.Result = -not $allowEmailIntoChannel
$auditResult.Details = "AllowEmailIntoChannel is set to $allowEmailIntoChannel"
$auditResult.FailureReason = if ($allowEmailIntoChannel) { "Emails can be sent to a channel email address" } else { "N/A" }
$auditResult.Status = if (-not $allowEmailIntoChannel) { "Pass" } else { "Fail" }
$auditResults += $auditResult # Prepare failure reasons and details based on compliance
$failureReasons = if ($allowEmailIntoChannel) {
"Emails can be sent to a channel email address" # Condition A Fail: AllowEmailIntoChannel is True
}
else {
"N/A" # Condition A Pass: AllowEmailIntoChannel is False
}
$details = if ($allowEmailIntoChannel) {
"AllowEmailIntoChannel is set to True" # Condition B Fail: Emails are allowed
}
else {
"AllowEmailIntoChannel is set to False" # Condition B Pass: Emails are blocked
}
# Create and populate the CISAuditResult object
$params = @{
Rec = $recnum
Result = -not $allowEmailIntoChannel
Status = if (-not $allowEmailIntoChannel) { "Pass" } else { "Fail" }
Details = $details
FailureReason = $failureReasons
}
$auditResult = Initialize-CISAuditResult @params
}
catch {
Write-Error "An error occurred during the test: $_"
# Retrieve the description from the test definitions
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
} }
end { end {
# Return auditResults # Return the audit result
return $auditResults return $auditResult
} }
} }

View File

@@ -1,51 +1,110 @@
function Test-BlockMailForwarding { function Test-BlockMailForwarding {
[CmdletBinding()] [CmdletBinding()]
[OutputType([CISAuditResult])]
param ( param (
# Parameters can be added if needed # Parameters can be added if needed
) )
begin { begin {
# Dot source the class script if necessary
#. .\source\Classes\CISAuditResult.ps1
# Initialization code, if needed
$recnum = "6.2.1"
$auditResult = [CISAuditResult]::new() <#
$auditResult.Rec = "6.2.1" Conditions for 6.2.1 (L1) Ensure all forms of mail forwarding are blocked and/or disabled
$auditResult.ELevel = "E3"
$auditResult.ProfileLevel = "L1" Validate test for a pass:
$auditResult.CISControlVer = "v8" - Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
$auditResult.CISControl = "0.0" - Specific conditions to check:
$auditResult.CISDescription = "Explicitly Not Mapped" - Condition A: Transport rules do not forward email to external domains.
$auditResult.IG1 = $false - Condition B: Anti-spam outbound policy is configured to disable automatic email forwarding to external domains.
$auditResult.IG2 = $false - Condition C: No exceptions to the forwarding rules unless explicitly defined by organizational policy.
$auditResult.IG3 = $false
$auditResult.RecDescription = "Ensure all forms of mail forwarding are blocked and/or disabled" Validate test for a fail:
- Confirm that the failure conditions in the automated test are consistent with the manual audit results.
- Specific conditions to check:
- Condition A: One or more transport rules forward email to external domains.
- Condition B: Anti-spam outbound policy does not disable automatic email forwarding to external domains.
- Condition C: Unapproved exceptions to the forwarding rules are present.
#>
} }
process { process {
# Verify that no rules are forwarding the email to external domains try {
$transportRules = Get-TransportRule | Where-Object { $_.RedirectMessageTo -ne $null } # 6.2.1 (L1) Ensure all forms of mail forwarding are blocked and/or disabled
$forwardingBlocked = $transportRules.Count -eq 0
$auditResult.Result = $forwardingBlocked # Step 1: Retrieve the transport rules that redirect messages
$auditResult.Details = if ($transportRules.Count -gt 0) { $transportRules = Get-TransportRule | Where-Object { $null -ne $_.RedirectMessageTo }
$transportRules | ForEach-Object { $transportForwardingBlocked = $transportRules.Count -eq 0
"$($_.Name) redirects to $($_.RedirectMessageTo)"
} -join " | " # Step 2: Check all anti-spam outbound policies
} else { $outboundSpamPolicies = Get-HostedOutboundSpamFilterPolicy
"Step 1: No forwarding rules found. Please proceed with Step 2 described in CIS Benchmark." $nonCompliantSpamPolicies = $outboundSpamPolicies | Where-Object { $_.AutoForwardingMode -ne 'Off' }
$nonCompliantSpamPoliciesArray = @($nonCompliantSpamPolicies)
$spamForwardingBlocked = $nonCompliantSpamPoliciesArray.Count -eq 0
# Determine overall compliance
$forwardingBlocked = $transportForwardingBlocked -and $spamForwardingBlocked
# Prepare failure reasons and details based on compliance
$failureReasons = @()
$details = @()
if ($transportRules.Count -gt 0) {
# Fail Condition A
$failureReasons += "Mail forwarding rules found: $($transportRules.Name -join ', ')"
$details += "Transport Rules Details:`nRule Name|Redirects To"
$details += $transportRules | ForEach-Object {
"$($_.Name)|$($_.RedirectMessageTo -join ', ')"
}
$details += "`n"
}
if ($nonCompliantSpamPoliciesArray.Count -gt 0) {
# Fail Condition B
$failureReasons += "Outbound spam policies allowing automatic forwarding found."
$details += "Outbound Spam Policies Details:`nPolicy|AutoForwardingMode"
$details += $nonCompliantSpamPoliciesArray | ForEach-Object {
"$($_.Name)|$($_.AutoForwardingMode)"
}
}
if ($failureReasons.Count -eq 0) {
$failureReasons = "N/A"
$details = "Both transport rules and outbound spam policies are configured correctly to block forwarding."
}
else {
$failureReasons = $failureReasons -join " | "
$details = $details -join "`n"
}
# Populate the audit result
$params = @{
Rec = $recnum
Result = $forwardingBlocked
Status = if ($forwardingBlocked) { "Pass" } else { "Fail" }
Details = $details
FailureReason = $failureReasons
}
$auditResult = Initialize-CISAuditResult @params
} }
$auditResult.FailureReason = if (-not $forwardingBlocked) { catch {
"Mail forwarding rules found: $($transportRules.Name -join ', ')" Write-Error "An error occurred during the test: $_"
} else {
"N/A" # Retrieve the description from the test definitions
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
} }
$auditResult.Status = if ($forwardingBlocked) { "Pass" } else { "Fail" }
} }
end { end {
# Return the result object # Return the audit result
return $auditResult return $auditResult
} }
} }

View File

@@ -1,47 +1,86 @@
function Test-BlockSharedMailboxSignIn { function Test-BlockSharedMailboxSignIn {
[CmdletBinding()] [CmdletBinding()]
[OutputType([CISAuditResult])]
param ( param (
# Parameters can be added if needed # Parameters can be added if needed
) )
begin { begin {
# Dot source the class script # Dot source the class script if necessary
#. .\source\Classes\CISAuditResult.ps1
$auditResults = @() # Initialization code, if needed
$recnum = "1.2.2"
# Conditions for 1.2.2 (L1) Ensure sign-in to shared mailboxes is blocked
#
# Validate test for a pass:
# - Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
# - Specific conditions to check:
# - Condition A: No shared mailboxes have the "Sign-in blocked" option disabled in the properties pane on the Microsoft 365 admin center.
# - Condition B: Using PowerShell, the `AccountEnabled` property for all shared mailboxes is set to `False`.
#
# Validate test for a fail:
# - Confirm that the failure conditions in the automated test are consistent with the manual audit results.
# - Specific conditions to check:
# - Condition A: One or more shared mailboxes have the "Sign-in blocked" option enabled in the properties pane on the Microsoft 365 admin center.
# - Condition B: Using PowerShell, the `AccountEnabled` property for one or more shared mailboxes is set to `True`.
} }
process { process {
# 1.2.2 (L1) Ensure sign-in to shared mailboxes is blocked try {
# Pass if all shared mailboxes have AccountEnabled set to False. # Step: Retrieve shared mailbox details
# Fail if any shared mailbox has AccountEnabled set to True. $MBX = Get-EXOMailbox -RecipientTypeDetails SharedMailbox
$MBX = Get-EXOMailbox -RecipientTypeDetails SharedMailbox # Step: Retrieve details of shared mailboxes from Azure AD (Condition B: Pass/Fail)
$sharedMailboxDetails = $MBX | ForEach-Object { Get-AzureADUser -ObjectId $_.ExternalDirectoryObjectId } $sharedMailboxDetails = $MBX | ForEach-Object { Get-AzureADUser -ObjectId $_.ExternalDirectoryObjectId }
$enabledMailboxes = $sharedMailboxDetails | Where-Object { $_.AccountEnabled } | ForEach-Object { $_.DisplayName }
$allBlocked = $enabledMailboxes.Count -eq 0
# Create an instance of CISAuditResult and populate it # Step: Identify enabled mailboxes (Condition B: Pass/Fail)
$auditResult = [CISAuditResult]::new() $enabledMailboxes = $sharedMailboxDetails | Where-Object { $_.AccountEnabled } | ForEach-Object { $_.DisplayName }
$auditResult.CISControlVer = "v8" $allBlocked = $enabledMailboxes.Count -eq 0
$auditResult.CISControl = "0.0" # Control is explicitly not mapped
$auditResult.CISDescription = "Explicitly Not Mapped"
$auditResult.Rec = "1.2.2"
$auditResult.ELevel = "E3"
$auditResult.ProfileLevel = "L1"
$auditResult.IG1 = $false # Control is not mapped, hence IG1 is false
$auditResult.IG2 = $false # Control is not mapped, hence IG2 is false
$auditResult.IG3 = $false # Control is not mapped, hence IG3 is false
$auditResult.RecDescription = "Ensure sign-in to shared mailboxes is blocked"
$auditResult.Result = $allBlocked
$auditResult.Details = "Enabled Mailboxes: $($enabledMailboxes -join ', ')"
$auditResult.FailureReason = if ($allBlocked) { "N/A" } else { "Some mailboxes have sign-in enabled: $($enabledMailboxes -join ', ')" }
$auditResult.Status = if ($allBlocked) { "Pass" } else { "Fail" }
$auditResults += $auditResult # Step: Determine failure reasons based on enabled mailboxes (Condition A & B: Fail)
$failureReasons = if (-not $allBlocked) {
"Some mailboxes have sign-in enabled: $($enabledMailboxes -join ', ')"
}
else {
"N/A"
}
# Step: Prepare details for the audit result (Condition A & B: Pass/Fail)
$details = if ($allBlocked) {
"All shared mailboxes have sign-in blocked."
}
else {
"Enabled Mailboxes: $($enabledMailboxes -join ', ')"
}
# Step: Create and populate the CISAuditResult object
$params = @{
Rec = $recnum
Result = $allBlocked # Pass: Condition A, Condition B
Status = if ($allBlocked) { "Pass" } else { "Fail" }
Details = $details
FailureReason = $failureReasons
}
$auditResult = Initialize-CISAuditResult @params
}
catch {
Write-Error "An error occurred during the test: $_"
# Retrieve the description from the test definitions
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
} }
end { end {
# Return auditResults # Return the audit result
return $auditResults return $auditResult
} }
} }

View File

@@ -1,46 +1,89 @@
function Test-CommonAttachmentFilter { function Test-CommonAttachmentFilter {
[CmdletBinding()] [CmdletBinding()]
[OutputType([CISAuditResult])]
param ( param (
# Aligned
# Parameters can be added if needed # Parameters can be added if needed
) )
begin { begin {
<#
Conditions for 2.1.2 (L1) Ensure the Common Attachment Types Filter is enabled
$auditResults = @() Validate test for a pass:
- Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
- Specific conditions to check:
- Condition A: The Common Attachment Types Filter is enabled in the Microsoft 365 Security & Compliance Center.
- Condition B: Using Exchange Online PowerShell, verify that the `EnableFileFilter` property of the default malware filter policy is set to `True`.
- Condition C: Ensure that the setting is enabled in the highest priority policy listed if custom policies exist.
Validate test for a fail:
- Confirm that the failure conditions in the automated test are consistent with the manual audit results.
- Specific conditions to check:
- Condition A: The Common Attachment Types Filter is not enabled in the Microsoft 365 Security & Compliance Center.
- Condition B: Using Exchange Online PowerShell, verify that the `EnableFileFilter` property of the default malware filter policy is set to `False`.
- Condition C: Ensure that the setting is not enabled in the highest priority policy listed if custom policies exist.
#>
# Dot source the class script if necessary
#. .\source\Classes\CISAuditResult.ps1
# Initialization code, if needed
$recnum = "2.1.2"
} }
process { process {
# 2.1.2 (L1) Ensure the Common Attachment Types Filter is enabled try {
# Pass if EnableFileFilter is set to True. Fail otherwise. # 2.1.2 (L1) Ensure the Common Attachment Types Filter is enabled
# Condition A: The Common Attachment Types Filter is enabled in the Microsoft 365 Security & Compliance Center.
# Condition B: Using Exchange Online PowerShell, verify that the `EnableFileFilter` property of the default malware filter policy is set to `True`.
$attachmentFilter = Get-MalwareFilterPolicy -Identity Default | Select-Object EnableFileFilter # Retrieve the attachment filter policy
$result = $attachmentFilter.EnableFileFilter $attachmentFilter = Get-MalwareFilterPolicy -Identity Default | Select-Object EnableFileFilter
$details = "File Filter Enabled: $($attachmentFilter.EnableFileFilter)" $result = $attachmentFilter.EnableFileFilter
$failureReason = if ($result) { "N/A" } else { "Common Attachment Types Filter is disabled" }
$status = if ($result) { "Pass" } else { "Fail" }
# Create an instance of CISAuditResult and populate it # Prepare failure reasons and details based on compliance
$auditResult = [CISAuditResult]::new() $failureReasons = if (-not $result) {
$auditResult.Status = $status # Condition A: The Common Attachment Types Filter is not enabled in the Microsoft 365 Security & Compliance Center.
$auditResult.ELevel = "E3" # Condition B: Using Exchange Online PowerShell, verify that the `EnableFileFilter` property of the default malware filter policy is set to `False`.
$auditResult.ProfileLevel = "L1" "Common Attachment Types Filter is disabled"
$auditResult.Rec = "2.1.2" }
$auditResult.RecDescription = "Ensure the Common Attachment Types Filter is enabled" else {
$auditResult.CISControlVer = "v8" "N/A"
$auditResult.CISControl = "9.6" }
$auditResult.CISDescription = "Block Unnecessary File Types"
$auditResult.IG1 = $false
$auditResult.IG2 = $true
$auditResult.IG3 = $true
$auditResult.Result = $result
$auditResult.Details = $details
$auditResult.FailureReason = $failureReason
$auditResults += $auditResult $details = if ($result) {
"File Filter Enabled: True"
}
else {
"File Filter Enabled: False"
}
# Create and populate the CISAuditResult object
$params = @{
Rec = $recnum
Result = $result
Status = if ($result) { "Pass" } else { "Fail" }
Details = $details
FailureReason = $failureReasons
}
$auditResult = Initialize-CISAuditResult @params
}
catch {
Write-Error "An error occurred during the test: $_"
# Retrieve the description from the test definitions
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
} }
end { end {
# Return auditResults # Return the audit result
return $auditResults return $auditResult
} }
} }

View File

@@ -1,40 +1,83 @@
function Test-CustomerLockbox { function Test-CustomerLockbox {
[CmdletBinding()] [CmdletBinding()]
[OutputType([CISAuditResult])]
param ( param (
# Define your parameters here # Aligned
# Define your parameters here if needed
) )
begin { begin {
# Dot source the class script if necessary
#. .\source\Classes\CISAuditResult.ps1
$auditResults = @() # Initialization code, if needed
$recnum = "1.3.6"
# Conditions for 1.3.6 (L2) Ensure the customer lockbox feature is enabled (Automated)
#
# Validate test for a pass:
# - Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
# - Specific conditions to check:
# - Condition A: In the Microsoft 365 admin center, the box labeled "Customer Lockbox Requests" is checked.
# - Condition B: Using the SecureScore portal, the Customer Lockbox feature is enabled.
# - Condition C: Using PowerShell, the Customer Lockbox feature is set to `True`.
#
# Validate test for a fail:
# - Confirm that the failure conditions in the automated test are consistent with the manual audit results.
# - Specific conditions to check:
# - Condition A: In the Microsoft 365 admin center, the box labeled "Customer Lockbox Requests" is not checked.
# - Condition B: Using the SecureScore portal, the Customer Lockbox feature is not enabled.
# - Condition C: Using PowerShell, the Customer Lockbox feature is not set to `True`.
} }
process { process {
# 1.3.6 (L2) Ensure the customer lockbox feature is enabled try {
$orgConfig = Get-OrganizationConfig | Select-Object CustomerLockBoxEnabled # Step: Retrieve the organization configuration (Condition C: Pass/Fail)
$customerLockboxEnabled = $orgConfig.CustomerLockBoxEnabled $orgConfig = Get-OrganizationConfig | Select-Object CustomerLockBoxEnabled
$customerLockboxEnabled = $orgConfig.CustomerLockBoxEnabled
$auditResult = [CISAuditResult]::new() # Step: Prepare failure reasons and details based on compliance (Condition A, B, & C: Fail)
$auditResult.Status = if ($customerLockboxEnabled) { "Pass" } else { "Fail" } $failureReasons = if (-not $customerLockboxEnabled) {
$auditResult.ELevel = "E5" "Customer lockbox feature is not enabled."
$auditResult.ProfileLevel = "L2" }
$auditResult.Rec = "1.3.6" else {
$auditResult.RecDescription = "Ensure the customer lockbox feature is enabled" "N/A"
$auditResult.CISControlVer = 'v8' }
$auditResult.CISControl = "0.0" # As per the snapshot provided, this is explicitly not mapped
$auditResult.CISDescription = "Explicitly Not Mapped"
$auditResult.IG1 = $false
$auditResult.IG2 = $false
$auditResult.IG3 = $false
$auditResult.Result = $customerLockboxEnabled
$auditResult.Details = "Customer Lockbox Enabled: $customerLockboxEnabled"
$auditResult.FailureReason = if ($customerLockboxEnabled) { "N/A" } else { "Customer lockbox feature is not enabled." }
$auditResults += $auditResult # Step: Prepare details for the audit result (Condition A, B, & C: Pass/Fail)
$details = if ($customerLockboxEnabled) {
"Customer Lockbox Enabled: True"
}
else {
"Customer Lockbox Enabled: False"
}
# Step: Create and populate the CISAuditResult object
$params = @{
Rec = $recnum
Result = $customerLockboxEnabled
Status = if ($customerLockboxEnabled) { "Pass" } else { "Fail" }
Details = $details
FailureReason = $failureReasons
}
$auditResult = Initialize-CISAuditResult @params
}
catch {
Write-Error "An error occurred during the test: $_"
# Retrieve the description from the test definitions
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
} }
end { end {
# Return auditResults # Return the audit result
return $auditResults return $auditResult
} }
} }

View File

@@ -1,45 +1,82 @@
function Test-DialInBypassLobby { function Test-DialInBypassLobby {
[CmdletBinding()] [CmdletBinding()]
[OutputType([CISAuditResult])]
param ( param (
# Aligned
# Parameters can be defined here if needed # Parameters can be defined here if needed
) )
begin { begin {
# Dot source the class script # Dot source the class script if necessary
#. .\source\Classes\CISAuditResult.ps1
$auditResults = @() # Initialization code, if needed
$recnum = "8.5.4"
} }
process { process {
# 8.5.4 (L1) Ensure users dialing in can't bypass the lobby
# Connect to Teams PowerShell using Connect-MicrosoftTeams try {
# 8.5.4 (L1) Ensure users dialing in can't bypass the lobby
#
# Validate test for a pass:
# - Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
# - Specific conditions to check:
# - Condition A: The `AllowPSTNUsersToBypassLobby` setting in the Global Teams meeting policy is set to `False`.
# - Condition B: Verification using the UI in the Microsoft Teams admin center confirms that "People dialing in can't bypass the lobby" is set to `Off`.
# - Condition C: Ensure that individuals who dial in by phone must wait in the lobby until admitted by a meeting organizer, co-organizer, or presenter.
#
# Validate test for a fail:
# - Confirm that the failure conditions in the automated test are consistent with the manual audit results.
# - Specific conditions to check:
# - Condition A: The `AllowPSTNUsersToBypassLobby` setting in the Global Teams meeting policy is not set to `False`.
# - Condition B: Verification using the UI in the Microsoft Teams admin center shows that "People dialing in can't bypass the lobby" is not set to `Off`.
# - Condition C: Individuals who dial in by phone are able to join the meeting directly without waiting in the lobby.
$CsTeamsMeetingPolicyPSTN = Get-CsTeamsMeetingPolicy -Identity Global | Select-Object -Property AllowPSTNUsersToBypassLobby # Retrieve Teams meeting policy for PSTN users
$PSTNBypassDisabled = -not $CsTeamsMeetingPolicyPSTN.AllowPSTNUsersToBypassLobby $CsTeamsMeetingPolicyPSTN = Get-CsTeamsMeetingPolicy -Identity Global | Select-Object -Property AllowPSTNUsersToBypassLobby
$PSTNBypassDisabled = -not $CsTeamsMeetingPolicyPSTN.AllowPSTNUsersToBypassLobby
# Create an instance of CISAuditResult and populate it # Prepare failure reasons and details based on compliance
$auditResult = [CISAuditResult]::new() $failureReasons = if (-not $PSTNBypassDisabled) {
$auditResult.CISControlVer = "v8" "Users dialing in can bypass the lobby"
$auditResult.CISControl = "0.0" # Explicitly Not Mapped as per the image provided }
$auditResult.CISDescription = "Explicitly Not Mapped" else {
$auditResult.Rec = "8.5.4" "N/A"
$auditResult.ELevel = "E3" }
$auditResult.ProfileLevel = "L1"
$auditResult.IG1 = $false # Set based on the CIS Controls image
$auditResult.IG2 = $false # Set based on the CIS Controls image
$auditResult.IG3 = $false # Set based on the CIS Controls image
$auditResult.RecDescription = "Ensure users dialing in can't bypass the lobby"
$auditResult.Result = $PSTNBypassDisabled
$auditResult.Details = "AllowPSTNUsersToBypassLobby is set to $($CsTeamsMeetingPolicyPSTN.AllowPSTNUsersToBypassLobby)"
$auditResult.FailureReason = if ($PSTNBypassDisabled) { "N/A" } else { "Users dialing in can bypass the lobby" }
$auditResult.Status = if ($PSTNBypassDisabled) { "Pass" } else { "Fail" }
$auditResults += $auditResult $details = if ($PSTNBypassDisabled) {
"AllowPSTNUsersToBypassLobby is set to False"
}
else {
"AllowPSTNUsersToBypassLobby is set to True"
}
# Create and populate the CISAuditResult object
$params = @{
Rec = $recnum
Result = $PSTNBypassDisabled
Status = if ($PSTNBypassDisabled) { "Pass" } else { "Fail" }
Details = $details
FailureReason = $failureReasons
}
$auditResult = Initialize-CISAuditResult @params
}
catch {
Write-Error "An error occurred during the test: $_"
# Retrieve the description from the test definitions
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
} }
end { end {
# Return auditResults # Return the audit result
return $auditResults return $auditResult
} }
} }

View File

@@ -1,41 +1,85 @@
function Test-DisallowInfectedFilesDownload { function Test-DisallowInfectedFilesDownload {
[CmdletBinding()] [CmdletBinding()]
[OutputType([CISAuditResult])]
param ( param (
# Define your parameters here # Aligned
# Define your parameters here if needed
) )
begin { begin {
# Initialization code # Dot source the class script if necessary
#. .\source\Classes\CISAuditResult.ps1
$auditResult = [CISAuditResult]::new() # Initialization code, if needed
$recnum = "7.3.1"
} }
process { process {
# 7.3.1 (L2) Ensure Office 365 SharePoint infected files are disallowed for download
$SPOTenantDisallowInfectedFileDownload = Get-SPOTenant | Select-Object DisallowInfectedFileDownload
$isDisallowInfectedFileDownloadEnabled = $SPOTenantDisallowInfectedFileDownload.DisallowInfectedFileDownload
# Populate the auditResult object with the required properties try {
$auditResult.CISControlVer = "v8" # 7.3.1 (L2) Ensure Office 365 SharePoint infected files are disallowed for download
$auditResult.CISControl = "10.1" #
$auditResult.CISDescription = "Deploy and Maintain Anti-Malware Software" # Validate test for a pass:
# - Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
# - Specific conditions to check:
# - Condition A: The `DisallowInfectedFileDownload` setting is set to `True`.
# - Condition B: The setting prevents users from downloading infected files as detected by Defender for Office 365.
# - Condition C: Verification using the PowerShell command confirms that the setting is correctly configured.
#
# Validate test for a fail:
# - Confirm that the failure conditions in the automated test are consistent with the manual audit results.
# - Specific conditions to check:
# - Condition A: The `DisallowInfectedFileDownload` setting is not set to `True`.
# - Condition B: The setting does not prevent users from downloading infected files.
# - Condition C: Verification using the PowerShell command indicates that the setting is incorrectly configured.
$auditResult.Rec = "7.3.1" # Retrieve the SharePoint tenant configuration
$auditResult.ELevel = "E5" $SPOTenantDisallowInfectedFileDownload = Get-SPOTenant | Select-Object DisallowInfectedFileDownload
$auditResult.ProfileLevel = "L2"
$auditResult.IG1 = $true
$auditResult.IG2 = $true
$auditResult.IG3 = $true
$auditResult.RecDescription = "Ensure Office 365 SharePoint infected files are disallowed for download"
$auditResult.Result = $isDisallowInfectedFileDownloadEnabled # Condition A: The `DisallowInfectedFileDownload` setting is set to `True`
$auditResult.Details = "DisallowInfectedFileDownload: $($SPOTenantDisallowInfectedFileDownload.DisallowInfectedFileDownload)" $isDisallowInfectedFileDownloadEnabled = $SPOTenantDisallowInfectedFileDownload.DisallowInfectedFileDownload
$auditResult.FailureReason = if (-not $isDisallowInfectedFileDownloadEnabled) { "Downloading infected files is not disallowed." } else { "N/A" }
$auditResult.Status = if ($isDisallowInfectedFileDownloadEnabled) { "Pass" } else { "Fail" } # Prepare failure reasons and details based on compliance
$failureReasons = if (-not $isDisallowInfectedFileDownloadEnabled) {
"Downloading infected files is not disallowed." # Condition B: The setting does not prevent users from downloading infected files
}
else {
"N/A"
}
$details = if ($isDisallowInfectedFileDownloadEnabled) {
"DisallowInfectedFileDownload: True" # Condition C: Verification confirms the setting is correctly configured
}
else {
"DisallowInfectedFileDownload: False" # Condition C: Verification indicates the setting is incorrectly configured
}
# Create and populate the CISAuditResult object
$params = @{
Rec = $recnum
Result = $isDisallowInfectedFileDownloadEnabled
Status = if ($isDisallowInfectedFileDownloadEnabled) { "Pass" } else { "Fail" }
Details = $details
FailureReason = $failureReasons
}
$auditResult = Initialize-CISAuditResult @params
}
catch {
Write-Error "An error occurred during the test: $_"
# Retrieve the description from the test definitions
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
} }
end { end {
# Return auditResult # Return the audit result
return $auditResult return $auditResult
} }
} }

View File

@@ -1,44 +1,86 @@
function Test-EnableDKIM { function Test-EnableDKIM {
[CmdletBinding()] [CmdletBinding()]
[OutputType([CISAuditResult])]
param ( param (
# Aligned
# Parameters can be added if needed # Parameters can be added if needed
) )
begin { begin {
# Dot source the class script # Dot source the class script if necessary
#. .\source\Classes\CISAuditResult.ps1
$auditResults = @() # Initialization code, if needed
$recnum = "2.1.9"
<#
Conditions for 2.1.9 (L1) Ensure DKIM is enabled for all Exchange Online Domains (Automated)
Validate test for a pass:
- Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
- Specific conditions to check:
- Condition A: DKIM is enabled for all Exchange Online domains in the Microsoft 365 security center.
- Condition B: Using the Exchange Online PowerShell Module, the `CnameConfiguration.Enabled` property for each domain is set to `True`.
Validate test for a fail:
- Confirm that the failure conditions in the automated test are consistent with the manual audit results.
- Specific conditions to check:
- Condition A: DKIM is not enabled for one or more Exchange Online domains in the Microsoft 365 security center.
- Condition B: Using the Exchange Online PowerShell Module, the `CnameConfiguration.Enabled` property for one or more domains is set to `False`.
#>
} }
process { process {
# 2.1.9 (L1) Ensure DKIM is enabled for all Exchange Online Domains
# Pass if Enabled is True for all domains. Fail if any domain has Enabled set to False.
$dkimConfig = Get-DkimSigningConfig | Select-Object Domain, Enabled
$dkimResult = ($dkimConfig | ForEach-Object { $_.Enabled }) -notcontains $false
$dkimFailedDomains = $dkimConfig | Where-Object { -not $_.Enabled } | ForEach-Object { $_.Domain }
# Create an instance of CISAuditResult and populate it try {
$auditResult = [CISAuditResult]::new() # 2.1.9 (L1) Ensure DKIM is enabled for all Exchange Online Domains
$auditResult.Status = if ($dkimResult) { "Pass" } else { "Fail" }
$auditResult.ELevel = "E3"
$auditResult.ProfileLevel = "L1"
$auditResult.Rec = "2.1.9"
$auditResult.RecDescription = "Ensure that DKIM is enabled for all Exchange Online Domains"
$auditResult.CISControlVer = "v8"
$auditResult.CISControl = "9.5"
$auditResult.CISDescription = "Implement DMARC"
$auditResult.IG1 = $false
$auditResult.IG2 = $true
$auditResult.IG3 = $true
$auditResult.Result = $dkimResult
$auditResult.Details = if (-not $dkimResult) { "DKIM not enabled for: $($dkimFailedDomains -join ', ')" } else { "All domains have DKIM enabled" }
$auditResult.FailureReason = if (-not $dkimResult) { "DKIM is not enabled for some domains" } else { "N/A" }
$auditResults += $auditResult # Retrieve DKIM configuration for all domains
$dkimConfig = Get-DkimSigningConfig | Select-Object Domain, Enabled
$dkimResult = ($dkimConfig | ForEach-Object { $_.Enabled }) -notcontains $false
$dkimFailedDomains = $dkimConfig | Where-Object { -not $_.Enabled } | ForEach-Object { $_.Domain }
# Prepare failure reasons and details based on compliance
$failureReasons = if (-not $dkimResult) {
"DKIM is not enabled for some domains" # Condition A fail
}
else {
"N/A"
}
$details = if ($dkimResult) {
"All domains have DKIM enabled" # Condition A pass
}
else {
"DKIM not enabled for: $($dkimFailedDomains -join ', ')" # Condition B fail
}
# Create and populate the CISAuditResult object
$params = @{
Rec = $recnum
Result = $dkimResult
Status = if ($dkimResult) { "Pass" } else { "Fail" }
Details = $details
FailureReason = $failureReasons
}
$auditResult = Initialize-CISAuditResult @params
}
catch {
Write-Error "An error occurred during the test: $_"
# Retrieve the description from the test definitions
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
} }
end { end {
# Return auditResults # Return the audit result
return $auditResults return $auditResult
} }
} }

View File

@@ -1,45 +1,83 @@
function Test-ExternalNoControl { function Test-ExternalNoControl {
[CmdletBinding()] [CmdletBinding()]
[OutputType([CISAuditResult])]
param ( param (
# Aligned
# Parameters can be defined here if needed # Parameters can be defined here if needed
) )
begin { begin {
# Dot source the class script # Dot source the class script if necessary
# . .\source\Classes\CISAuditResult.ps1
$auditResults = @() # Initialization code, if needed
$recnum = "8.5.7"
} }
process { process {
# 8.5.7 (L1) Ensure external participants can't give or request control
# Connect to Teams PowerShell using Connect-MicrosoftTeams try {
# 8.5.7 (L1) Ensure external participants can't give or request control
#
# Validate test for a pass:
# - Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
# - Specific conditions to check:
# - Condition A: Ensure the `AllowExternalParticipantGiveRequestControl` setting in Teams is set to `False`.
# - Condition B: The setting is verified through the Microsoft Teams admin center or via PowerShell command.
# - Condition C: Verification using the UI confirms that external participants are unable to give or request control.
#
# Validate test for a fail:
# - Confirm that the failure conditions in the automated test are consistent with the manual audit results.
# - Specific conditions to check:
# - Condition A: The `AllowExternalParticipantGiveRequestControl` setting in Teams is not set to `False`.
# - Condition B: The setting is verified through the Microsoft Teams admin center or via PowerShell command.
# - Condition C: Verification using the UI indicates that external participants can give or request control.
$CsTeamsMeetingPolicyControl = Get-CsTeamsMeetingPolicy -Identity Global | Select-Object -Property AllowExternalParticipantGiveRequestControl # Retrieve Teams meeting policy for external participant control
$externalControlRestricted = -not $CsTeamsMeetingPolicyControl.AllowExternalParticipantGiveRequestControl $CsTeamsMeetingPolicyControl = Get-CsTeamsMeetingPolicy -Identity Global | Select-Object -Property AllowExternalParticipantGiveRequestControl
$externalControlRestricted = -not $CsTeamsMeetingPolicyControl.AllowExternalParticipantGiveRequestControl
# Create an instance of CISAuditResult and populate it # Prepare failure reasons and details based on compliance
$auditResult = [CISAuditResult]::new() $failureReasons = if (-not $externalControlRestricted) {
$auditResult.CISControlVer = "v8" "External participants can give or request control"
$auditResult.CISControl = "0.0" # Explicitly Not Mapped as per the image provided }
$auditResult.CISDescription = "Explicitly Not Mapped" else {
$auditResult.Rec = "8.5.7" "N/A"
$auditResult.ELevel = "E3" }
$auditResult.ProfileLevel = "L1"
$auditResult.IG1 = $false # Set based on the CIS Controls image
$auditResult.IG2 = $false # Set based on the CIS Controls image
$auditResult.IG3 = $false # Set based on the CIS Controls image
$auditResult.RecDescription = "Ensure external participants can't give or request control"
$auditResult.Result = $externalControlRestricted
$auditResult.Details = "AllowExternalParticipantGiveRequestControl is set to $($CsTeamsMeetingPolicyControl.AllowExternalParticipantGiveRequestControl)"
$auditResult.FailureReason = if ($externalControlRestricted) { "N/A" } else { "External participants can give or request control" }
$auditResult.Status = if ($externalControlRestricted) { "Pass" } else { "Fail" }
$auditResults += $auditResult $details = if ($externalControlRestricted) {
"AllowExternalParticipantGiveRequestControl is set to False"
}
else {
"AllowExternalParticipantGiveRequestControl is set to True"
}
# Create and populate the CISAuditResult object
$params = @{
Rec = $recnum
Result = $externalControlRestricted
Status = if ($externalControlRestricted) { "Pass" } else { "Fail" }
Details = $details
FailureReason = $failureReasons
}
$auditResult = Initialize-CISAuditResult @params
}
catch {
Write-Error "An error occurred during the test: $_"
# Retrieve the description from the test definitions
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
} }
end { end {
# Return auditResults # Return the audit result
return $auditResults return $auditResult
} }
} }

View File

@@ -1,53 +1,90 @@
function Test-ExternalSharingCalendars { function Test-ExternalSharingCalendars {
[CmdletBinding()] [CmdletBinding()]
[OutputType([CISAuditResult])]
param ( param (
# Aligned
# Parameters can be added if needed # Parameters can be added if needed
) )
begin { begin {
# Dot source the class script # Dot source the class script if necessary
#. .\source\Classes\CISAuditResult.ps1
$auditResults = @() # Initialization code, if needed
$recnum = "1.3.3"
# Conditions for 1.3.3 (L2) Ensure 'External sharing' of calendars is not available (Automated)
#
# Validate test for a pass:
# - Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
# - Specific conditions to check:
# - Condition A: In the Microsoft 365 admin center, external calendar sharing is disabled.
# - Condition B: Using the Exchange Online PowerShell Module, the `OrganizationConfig` property `ExternalSharingEnabled` is set to `False`.
#
# Validate test for a fail:
# - Confirm that the failure conditions in the automated test are consistent with the manual audit results.
# - Specific conditions to check:
# - Condition A: In the Microsoft 365 admin center, external calendar sharing is enabled.
# - Condition B: Using the Exchange Online PowerShell Module, the `OrganizationConfig` property `ExternalSharingEnabled` is set to `True`.
} }
process { process {
# 1.3.3 (L2) Ensure 'External sharing' of calendars is not available (Automated) try {
$sharingPolicies = Get-SharingPolicy | Where-Object { $_.Domains -like '*CalendarSharing*' } # Step: Retrieve sharing policies related to calendar sharing
$sharingPolicies = Get-SharingPolicy | Where-Object { $_.Domains -like '*CalendarSharing*' }
# Check if calendar sharing is disabled in all applicable policies # Step (Condition A & B: Pass/Fail): Check if calendar sharing is disabled in all applicable policies
$isExternalSharingDisabled = $true $isExternalSharingDisabled = $true
$sharingPolicyDetails = @() $sharingPolicyDetails = @()
foreach ($policy in $sharingPolicies) { foreach ($policy in $sharingPolicies) {
if ($policy.Enabled -eq $true) { if ($policy.Enabled -eq $true) {
$isExternalSharingDisabled = $false $isExternalSharingDisabled = $false
$sharingPolicyDetails += "$($policy.Name): Enabled" $sharingPolicyDetails += "$($policy.Name): Enabled"
}
} }
# Step: Prepare failure reasons and details based on compliance (Condition A & B: Fail)
$failureReasons = if (-not $isExternalSharingDisabled) {
"Calendar sharing with external users is enabled in one or more policies."
}
else {
"N/A"
}
# Step: Prepare details for the audit result (Condition A & B: Pass/Fail)
$details = if ($isExternalSharingDisabled) {
"Calendar sharing with external users is disabled."
}
else {
"Enabled Sharing Policies: $($sharingPolicyDetails -join ', ')"
}
# Step: Create and populate the CISAuditResult object
$params = @{
Rec = $recnum
Result = $isExternalSharingDisabled
Status = if ($isExternalSharingDisabled) { "Pass" } else { "Fail" }
Details = $details
FailureReason = $failureReasons
}
$auditResult = Initialize-CISAuditResult @params
} }
catch {
Write-Error "An error occurred during the test: $_"
# Create an instance of CISAuditResult and populate it # Retrieve the description from the test definitions
$auditResult = [CISAuditResult]::new() $testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$auditResult.Rec = "1.3.3" $description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$auditResult.RecDescription = "Ensure 'External sharing' of calendars is not available"
$auditResult.ELevel = "E3"
$auditResult.ProfileLevel = "L2"
# The following IG values are placeholders. Replace with actual values when known.
$auditResult.IG1 = $false
$auditResult.IG2 = $true
$auditResult.IG3 = $true
$auditResult.CISControlVer = "v8"
# Placeholder for CIS Control, to be replaced with the actual value when available
$auditResult.CISControl = "4.8"
$auditResult.CISDescription = "Uninstall or Disable Unnecessary Services on Enterprise Assets and Software"
$auditResult.Result = $isExternalSharingDisabled
$auditResult.Details = if ($isExternalSharingDisabled) { "Calendar sharing with external users is disabled." } else { "Enabled Sharing Policies: $($sharingPolicyDetails -join ', ')" }
$auditResult.FailureReason = if ($isExternalSharingDisabled) { "N/A" } else { "Calendar sharing with external users is enabled in one or more policies." }
$auditResult.Status = if ($isExternalSharingDisabled) { "Pass" } else { "Fail" }
$auditResults += $auditResult $script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
} }
end { end {
# Return auditResults # Return the audit result
return $auditResults return $auditResult
} }
} }

View File

@@ -1,46 +1,89 @@
function Test-GlobalAdminsCount { function Test-GlobalAdminsCount {
[CmdletBinding()] [CmdletBinding()]
[OutputType([CISAuditResult])]
param ( param (
# Define your parameters here # Define your parameters here if needed
) )
begin { begin {
# Dot source the class script # Dot source the class script if necessary
#. .\source\Classes\CISAuditResult.ps1
# Conditions for 1.1.3 (L1) Ensure that between two and four global admins are designated
#
# Validate test for a pass:
# - Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
# - Specific conditions to check:
# - Condition A: The number of global admins is at least 2.
# - Condition B: The number of global admins is at most 4.
# - Condition C: The list of global admin usernames is accurately retrieved and displayed.
#
# Validate test for a fail:
# - Confirm that the failure conditions in the automated test are consistent with the manual audit results.
# - Specific conditions to check:
# - Condition A: The number of global admins is less than 2.
# - Condition B: The number of global admins is more than 4.
# - Condition C: Any discrepancies or errors in retrieving the list of global admin usernames.
$auditResults = @() # Initialization code, if needed
$recnum = "1.1.3"
} }
process { process {
# 1.1.3 (L1) Ensure that between two and four global admins are designated try {
# Pass if the count of global admins is between 2 and 4. Fail otherwise. # Step: Retrieve global admin role
$globalAdminRole = Get-MgDirectoryRole -Filter "RoleTemplateId eq '62e90394-69f5-4237-9190-012177145e10'"
$globalAdminRole = Get-MgDirectoryRole -Filter "RoleTemplateId eq '62e90394-69f5-4237-9190-012177145e10'" # Step: Retrieve global admin members
$globalAdmins = Get-MgDirectoryRoleMember -DirectoryRoleId $globalAdminRole.Id $globalAdmins = Get-MgDirectoryRoleMember -DirectoryRoleId $globalAdminRole.Id
$globalAdminCount = $globalAdmins.AdditionalProperties.Count
$globalAdminUsernames = ($globalAdmins | ForEach-Object { $_.AdditionalProperties["displayName"] }) -join ', '
# Create an instance of CISAuditResult and populate it # Step: Count the number of global admins
$auditResult = [CISAuditResult]::new() $globalAdminCount = $globalAdmins.Count
$auditResult.CISControlVer = "v8"
$auditResult.CISControl = "5.1"
$auditResult.CISDescription = "Establish and Maintain an Inventory of Accounts"
$auditResult.Rec = "1.1.3"
$auditResult.ELevel = "E3" # Based on your environment (E3, E5, etc.)
$auditResult.ProfileLevel = "L1"
$auditResult.IG1 = $true # Set based on the benchmark
$auditResult.IG2 = $true # Set based on the benchmark
$auditResult.IG3 = $true # Set based on the benchmark
$auditResult.RecDescription = "Ensure that between two and four global admins are designated"
$auditResult.Result = $globalAdminCount -ge 2 -and $globalAdminCount -le 4
$auditResult.Details = "Count: $globalAdminCount; Users: $globalAdminUsernames"
$auditResult.FailureReason = if ($globalAdminCount -lt 2) { "Less than 2 global admins: $globalAdminUsernames" } elseif ($globalAdminCount -gt 4) { "More than 4 global admins: $globalAdminUsernames" } else { "N/A" }
$auditResult.Status = if ($globalAdminCount -ge 2 -and $globalAdminCount -le 4) { "Pass" } else { "Fail" }
$auditResults += $auditResult # Step: Retrieve and format the usernames of global admins
$globalAdminUsernames = ($globalAdmins | ForEach-Object {
"$($_.AdditionalProperties["displayName"]) ($($_.AdditionalProperties["userPrincipalName"]))"
}) -join ', '
# Step: Determine failure reasons based on global admin count
$failureReasons = if ($globalAdminCount -lt 2) {
"Less than 2 global admins: $globalAdminUsernames"
}
elseif ($globalAdminCount -gt 4) {
"More than 4 global admins: $globalAdminUsernames"
}
else {
"N/A"
}
# Step: Prepare details for the audit result
$details = "Count: $globalAdminCount; Users: $globalAdminUsernames"
# Step: Create and populate the CISAuditResult object
$params = @{
Rec = $recnum
Result = $globalAdminCount -ge 2 -and $globalAdminCount -le 4
Status = if ($globalAdminCount -ge 2 -and $globalAdminCount -le 4) { "Pass" } else { "Fail" }
Details = $details
FailureReason = $failureReasons
}
$auditResult = Initialize-CISAuditResult @params
}
catch {
Write-Error "An error occurred during the test: $_"
# Retrieve the description from the test definitions
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
} }
end { end {
# Return auditResults # Return the audit result
return $auditResults return $auditResult
} }
} }

View File

@@ -1,41 +1,78 @@
function Test-GuestAccessExpiration { function Test-GuestAccessExpiration {
[CmdletBinding()] [CmdletBinding()]
[OutputType([CISAuditResult])]
param ( param (
# Define your parameters here # Aligned
# Define your parameters here if needed
) )
begin { begin {
# Initialization code # Dot source the class script if necessary
#. .\source\Classes\CISAuditResult.ps1
$auditResult = [CISAuditResult]::new() # Initialization code, if needed
$recnum = "7.2.9"
} }
process { process {
# 7.2.9 (L1) Ensure guest access to a site or OneDrive will expire automatically
$SPOTenantGuestAccess = Get-SPOTenant | Select-Object ExternalUserExpirationRequired, ExternalUserExpireInDays
$isGuestAccessExpirationConfiguredCorrectly = $SPOTenantGuestAccess.ExternalUserExpirationRequired -and $SPOTenantGuestAccess.ExternalUserExpireInDays -le 30
# Populate the auditResult object with the required properties try {
$auditResult.CISControlVer = "v8" # 7.2.9 (L1) Ensure guest access to a site or OneDrive will expire automatically
$auditResult.CISControl = "0.0" #
$auditResult.CISDescription = "Explicitly Not Mapped" # Validate test for a pass:
# - Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
# - Specific conditions to check:
# - Condition A: The ExternalUserExpirationRequired setting in SharePoint is set to True.
# - Condition B: The ExternalUserExpireInDays setting in SharePoint is configured to 30 or less.
# - Condition C: Verification using the SharePoint Admin Center confirms that guest access will expire automatically after the specified number of days.
#
# Validate test for a fail:
# - Confirm that the failure conditions in the automated test are consistent with the manual audit results.
# - Specific conditions to check:
# - Condition A: The ExternalUserExpirationRequired setting in SharePoint is not set to True.
# - Condition B: The ExternalUserExpireInDays setting in SharePoint is configured to more than 30 days.
# - Condition C: Verification using the SharePoint Admin Center indicates that guest access is not set to expire automatically after the specified number of days.
$auditResult.Rec = "7.2.9" # Retrieve SharePoint tenant settings related to guest access expiration
$auditResult.ELevel = "E3" $SPOTenantGuestAccess = Get-SPOTenant | Select-Object ExternalUserExpirationRequired, ExternalUserExpireInDays
$auditResult.ProfileLevel = "L1" $isGuestAccessExpirationConfiguredCorrectly = $SPOTenantGuestAccess.ExternalUserExpirationRequired -and $SPOTenantGuestAccess.ExternalUserExpireInDays -le 30
$auditResult.IG1 = $false
$auditResult.IG2 = $false
$auditResult.IG3 = $false
$auditResult.RecDescription = "Ensure guest access to a site or OneDrive will expire automatically"
$auditResult.Result = $isGuestAccessExpirationConfiguredCorrectly # Prepare failure reasons and details based on compliance
$auditResult.Details = "ExternalUserExpirationRequired: $($SPOTenantGuestAccess.ExternalUserExpirationRequired); ExternalUserExpireInDays: $($SPOTenantGuestAccess.ExternalUserExpireInDays)" $failureReasons = if (-not $isGuestAccessExpirationConfiguredCorrectly) {
$auditResult.FailureReason = if (-not $isGuestAccessExpirationConfiguredCorrectly) { "Guest access expiration is not configured to automatically expire within 30 days or less." } else { "N/A" } "Guest access expiration is not configured to automatically expire within 30 days or less."
$auditResult.Status = if ($isGuestAccessExpirationConfiguredCorrectly) { "Pass" } else { "Fail" } }
else {
"N/A"
}
$details = "ExternalUserExpirationRequired: $($SPOTenantGuestAccess.ExternalUserExpirationRequired); ExternalUserExpireInDays: $($SPOTenantGuestAccess.ExternalUserExpireInDays)"
# Create and populate the CISAuditResult object
$params = @{
Rec = $recnum
Result = $isGuestAccessExpirationConfiguredCorrectly
Status = if ($isGuestAccessExpirationConfiguredCorrectly) { "Pass" } else { "Fail" }
Details = $details
FailureReason = $failureReasons
}
$auditResult = Initialize-CISAuditResult @params
}
catch {
Write-Error "An error occurred during the test: $_"
# Retrieve the description from the test definitions
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
} }
end { end {
# Return auditResult # Return the audit result
return $auditResult return $auditResult
} }
} }

View File

@@ -0,0 +1,70 @@
function Test-GuestUsersBiweeklyReview {
[CmdletBinding()]
[OutputType([CISAuditResult])]
param (
# Aligned
# Define your parameters here if needed
)
begin {
# Dot source the class script if necessary
#. .\source\Classes\CISAuditResult.ps1
# Initialization code, if needed
$recnum = "1.1.4"
}
process {
try {
# 1.1.4 (L1) Ensure Guest Users are reviewed at least biweekly
# Retrieve guest users from Microsoft Graph
# Connect-MgGraph -Scopes "User.Read.All"
$guestUsers = Get-MgUser -All -Filter "UserType eq 'Guest'"
# Prepare failure reasons and details based on compliance
$failureReasons = if ($guestUsers) {
"Guest users present: $($guestUsers.Count)"
}
else {
"N/A"
}
$details = if ($guestUsers) {
$auditCommand = "Get-MgUser -All -Property UserType,UserPrincipalName | Where {`$_.UserType -ne 'Member'} | Format-Table UserPrincipalName, UserType"
"Manual review required. To list guest users, run: `"$auditCommand`"."
}
else {
"No guest users found."
}
# Create and populate the CISAuditResult object
$params = @{
Rec = $recnum
Result = -not $guestUsers
Status = if ($guestUsers) { "Fail" } else { "Pass" }
Details = $details
FailureReason = $failureReasons
}
$auditResult = Initialize-CISAuditResult @params
}
catch {
Write-Error "An error occurred during the test: $_"
# Retrieve the description from the test definitions
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
}
end {
# Return the audit result
return $auditResult
}
}

View File

@@ -1,44 +1,82 @@
function Test-IdentifyExternalEmail { function Test-IdentifyExternalEmail {
[CmdletBinding()] [CmdletBinding()]
[OutputType([CISAuditResult])]
param ( param (
# Aligned
# Parameters can be defined here if needed # Parameters can be defined here if needed
) )
begin { begin {
# Dot source the class script # Dot source the class script if necessary
#. .\source\Classes\CISAuditResult.ps1
$auditResults = @() # Initialization code, if needed
$recnum = "6.2.3"
# Conditions for 6.2.3 (L1) Ensure email from external senders is identified
#
# Validate test for a pass:
# - Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
# - Specific conditions to check:
# - Condition A: External tagging is enabled using PowerShell for all identities.
# - Condition B: The BypassAllowList only contains email addresses the organization has permitted to bypass external tagging.
# - Condition C: External sender tag appears in email messages received from external sources.
#
# Validate test for a fail:
# - Confirm that the failure conditions in the automated test are consistent with the manual audit results.
# - Specific conditions to check:
# - Condition A: External tagging is not enabled using PowerShell for all identities.
# - Condition B: The BypassAllowList contains unauthorized email addresses.
# - Condition C: External sender tag does not appear in email messages received from external sources.
} }
process { process {
# 6.2.3 (L1) Ensure email from external senders is identified
# Requirement is to have external sender tagging enabled
$externalInOutlook = Get-ExternalInOutlook try {
$externalTaggingEnabled = ($externalInOutlook | ForEach-Object { $_.Enabled }) -contains $true # 6.2.3 (L1) Ensure email from external senders is identified
# Create an instance of CISAuditResult and populate it # Retrieve external sender tagging configuration
$auditResult = [CISAuditResult]::new() $externalInOutlook = Get-ExternalInOutlook
$auditResult.Status = if ($externalTaggingEnabled) { "Pass" } else { "Fail" } $externalTaggingEnabled = ($externalInOutlook | ForEach-Object { $_.Enabled }) -contains $true
$auditResult.ELevel = "E3"
$auditResult.ProfileLevel = "L1"
$auditResult.Rec = "6.2.3"
$auditResult.RecDescription = "Ensure email from external senders is identified"
$auditResult.CISControlVer = "v8"
$auditResult.CISControl = "0.0"
$auditResult.CISDescription = "Explicitly Not Mapped"
$auditResult.IG1 = $false
$auditResult.IG2 = $false
$auditResult.IG3 = $false
$auditResult.Result = $externalTaggingEnabled
$auditResult.Details = "Enabled: $($externalTaggingEnabled); AllowList: $($externalInOutlook.AllowList)"
$auditResult.FailureReason = if (-not $externalTaggingEnabled) { "External sender tagging is disabled" } else { "N/A" }
$auditResults += $auditResult # Prepare failure reasons and details based on compliance
$failureReasons = if (-not $externalTaggingEnabled) {
# Condition A: External tagging is not enabled using PowerShell for all identities.
"External sender tagging is disabled"
}
else {
"N/A"
}
# Details for external tagging configuration
$details = "Enabled: $($externalTaggingEnabled); AllowList: $($externalInOutlook.AllowList)"
# Create and populate the CISAuditResult object
$params = @{
Rec = $recnum
Result = $externalTaggingEnabled
Status = if ($externalTaggingEnabled) { "Pass" } else { "Fail" }
Details = $details
FailureReason = $failureReasons
}
$auditResult = Initialize-CISAuditResult @params
}
catch {
Write-Error "An error occurred during the test: $_"
# Retrieve the description from the test definitions
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
} }
end { end {
# Return auditResults # Return the audit result
return $auditResults return $auditResult
} }
} }

View File

@@ -1,42 +1,78 @@
function Test-LinkSharingRestrictions { function Test-LinkSharingRestrictions {
[CmdletBinding()] [CmdletBinding()]
[OutputType([CISAuditResult])]
param ( param (
# Aligned
# Define your parameters here # Define your parameters here
# Test behavior in prod # Test behavior in prod
) )
begin { begin {
# Initialization code # Dot source the class script if necessary
#. .\source\Classes\CISAuditResult.ps1
$auditResult = [CISAuditResult]::new() # Initialization code, if needed
$recnum = "7.2.7"
} }
process { process {
# 7.2.7 (L1) Ensure link sharing is restricted in SharePoint and OneDrive try {
$SPOTenantLinkSharing = Get-SPOTenant | Select-Object DefaultSharingLinkType # 7.2.7 (L1) Ensure link sharing is restricted in SharePoint and OneDrive
$isLinkSharingRestricted = $SPOTenantLinkSharing.DefaultSharingLinkType -eq 'Direct' # Or 'SpecificPeople' as per the recommendation #
# Validate test for a pass:
# - Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
# - Specific conditions to check:
# - Condition A: The `DefaultSharingLinkType` setting in SharePoint and OneDrive is set to `Direct`.
# - Condition B: The setting `Choose the type of link that's selected by default when users share files and folders in SharePoint and OneDrive` is set to `Specific people (only the people the user specifies)`.
# - Condition C: Verification using the UI confirms that the link sharing settings are configured as recommended.
#
# Validate test for a fail:
# - Confirm that the failure conditions in the automated test are consistent with the manual audit results.
# - Specific conditions to check:
# - Condition A: The `DefaultSharingLinkType` setting in SharePoint and OneDrive is not set to `Direct`.
# - Condition B: The setting `Choose the type of link that's selected by default when users share files and folders in SharePoint and OneDrive` is not set to `Specific people (only the people the user specifies)`.
# - Condition C: Verification using the UI indicates that the link sharing settings are not configured as recommended.
# Populate the auditResult object with the required properties # Retrieve link sharing configuration for SharePoint and OneDrive
$auditResult.CISControlVer = "v8" $SPOTenantLinkSharing = Get-SPOTenant | Select-Object DefaultSharingLinkType
$auditResult.CISControl = "3.3" $isLinkSharingRestricted = $SPOTenantLinkSharing.DefaultSharingLinkType -eq 'Direct' # Or 'SpecificPeople' as per the recommendation
$auditResult.CISDescription = "Configure Data Access Control Lists"
$auditResult.Rec = "7.2.7" # Prepare failure reasons and details based on compliance
$auditResult.ELevel = "E3" $failureReasons = if (-not $isLinkSharingRestricted) {
$auditResult.ProfileLevel = "L1" "Link sharing is not restricted to 'Specific people'. Current setting: $($SPOTenantLinkSharing.DefaultSharingLinkType)"
$auditResult.IG1 = $true }
$auditResult.IG2 = $true else {
$auditResult.IG3 = $true "N/A"
$auditResult.RecDescription = "Ensure link sharing is restricted in SharePoint and OneDrive" }
$auditResult.Result = $isLinkSharingRestricted $details = "DefaultSharingLinkType: $($SPOTenantLinkSharing.DefaultSharingLinkType)"
$auditResult.Details = "DefaultSharingLinkType: $($SPOTenantLinkSharing.DefaultSharingLinkType)"
$auditResult.FailureReason = if (-not $isLinkSharingRestricted) { "Link sharing is not restricted to 'Specific people'. Current setting: $($SPOTenantLinkSharing.DefaultSharingLinkType)" } else { "N/A" } # Create and populate the CISAuditResult object
$auditResult.Status = if ($isLinkSharingRestricted) { "Pass" } else { "Fail" } $params = @{
Rec = $recnum
Result = $isLinkSharingRestricted
Status = if ($isLinkSharingRestricted) { "Pass" } else { "Fail" }
Details = $details
FailureReason = $failureReasons
}
$auditResult = Initialize-CISAuditResult @params
}
catch {
Write-Error "An error occurred during the test: $_"
# Retrieve the description from the test definitions
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
} }
end { end {
# Return auditResult # Return the audit result
return $auditResult return $auditResult
} }
} }

View File

@@ -1,43 +1,90 @@
function Test-MailTipsEnabled { function Test-MailTipsEnabled {
[CmdletBinding()] [CmdletBinding()]
[OutputType([CISAuditResult])]
param ( param (
# Define your parameters here # Aligned
# Parameters can be added if needed
) )
begin { begin {
# Initialization code # Dot source the class script if necessary
#. .\source\Classes\CISAuditResult.ps1
# Initialization code, if needed
$auditResult = [CISAuditResult]::new() $auditResult = [CISAuditResult]::new()
$recnum = "6.5.2"
# Conditions for 6.5.2 (L2) Ensure MailTips are enabled for end users
#
# Validate test for a pass:
# - Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
# - Specific conditions to check:
# - Condition A: MailTipsAllTipsEnabled is set to True.
# - Condition B: MailTipsExternalRecipientsTipsEnabled is set to True.
# - Condition C: MailTipsGroupMetricsEnabled is set to True.
# - Condition D: MailTipsLargeAudienceThreshold is set to an acceptable value (default is 25).
#
# Validate test for a fail:
# - Confirm that the failure conditions in the automated test are consistent with the manual audit results.
# - Specific conditions to check:
# - Condition A: MailTipsAllTipsEnabled is not set to True.
# - Condition B: MailTipsExternalRecipientsTipsEnabled is not set to True.
# - Condition C: MailTipsGroupMetricsEnabled is not set to True.
# - Condition D: MailTipsLargeAudienceThreshold is not set to an acceptable value (default is 25).
} }
process { process {
# 6.5.2 (L2) Ensure MailTips are enabled for end users try {
$orgConfig = Get-OrganizationConfig | Select-Object MailTipsAllTipsEnabled, MailTipsExternalRecipientsTipsEnabled, MailTipsGroupMetricsEnabled, MailTipsLargeAudienceThreshold # 6.5.2 (L2) Ensure MailTips are enabled for end users
$allTipsEnabled = $orgConfig.MailTipsAllTipsEnabled -and $orgConfig.MailTipsGroupMetricsEnabled -and $orgConfig.MailTipsLargeAudienceThreshold -eq 25
$externalRecipientsTipsEnabled = $orgConfig.MailTipsExternalRecipientsTipsEnabled
# Since there is no direct CIS Control mapping, the control will be set as not applicable. # Retrieve organization configuration for MailTips settings
$auditResult.CISControl = "0" $orgConfig = Get-OrganizationConfig | Select-Object MailTipsAllTipsEnabled, MailTipsExternalRecipientsTipsEnabled, MailTipsGroupMetricsEnabled, MailTipsLargeAudienceThreshold
$auditResult.CISControlVer = "v8"
$auditResult.CISDescription = "Explicitly Not Mapped"
$auditResult.Rec = "6.5.2" # Check the MailTips settings (Conditions A, B, C, D)
$auditResult.ELevel = "E3" $allTipsEnabled = $orgConfig.MailTipsAllTipsEnabled -and $orgConfig.MailTipsGroupMetricsEnabled -and $orgConfig.MailTipsLargeAudienceThreshold -eq 25
$auditResult.ProfileLevel = "L2" $externalRecipientsTipsEnabled = $orgConfig.MailTipsExternalRecipientsTipsEnabled
$auditResult.IG1 = $false
$auditResult.IG2 = $false
$auditResult.IG3 = $false
$auditResult.RecDescription = "Ensure MailTips are enabled for end users"
$auditResult.Result = $allTipsEnabled -and $externalRecipientsTipsEnabled # Prepare failure reasons and details based on compliance
$auditResult.Details = "MailTipsAllTipsEnabled: $($orgConfig.MailTipsAllTipsEnabled); MailTipsExternalRecipientsTipsEnabled: $($orgConfig.MailTipsExternalRecipientsTipsEnabled); MailTipsGroupMetricsEnabled: $($orgConfig.MailTipsGroupMetricsEnabled); MailTipsLargeAudienceThreshold: $($orgConfig.MailTipsLargeAudienceThreshold)" $failureReasons = if (-not ($allTipsEnabled -and $externalRecipientsTipsEnabled)) {
$auditResult.FailureReason = if (-not $auditResult.Result) { "One or more MailTips settings are not configured as required." } else { "N/A" } "One or more MailTips settings are not configured as required."
$auditResult.Status = if ($auditResult.Result) { "Pass" } else { "Fail" } }
else {
"N/A"
}
$details = if ($allTipsEnabled -and $externalRecipientsTipsEnabled) {
"MailTipsAllTipsEnabled: $($orgConfig.MailTipsAllTipsEnabled); MailTipsExternalRecipientsTipsEnabled: $($orgConfig.MailTipsExternalRecipientsTipsEnabled); MailTipsGroupMetricsEnabled: $($orgConfig.MailTipsGroupMetricsEnabled); MailTipsLargeAudienceThreshold: $($orgConfig.MailTipsLargeAudienceThreshold)"
}
else {
"One or more MailTips settings are not configured as required."
}
# Create and populate the CISAuditResult object
$params = @{
Rec = $recnum
Result = $allTipsEnabled -and $externalRecipientsTipsEnabled
Status = if ($allTipsEnabled -and $externalRecipientsTipsEnabled) { "Pass" } else { "Fail" }
Details = $details
FailureReason = $failureReasons
}
$auditResult = Initialize-CISAuditResult @params
}
catch {
Write-Error "An error occurred during the test: $_"
# Retrieve the description from the test definitions
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
} }
end { end {
# Return auditResult # Return the audit result
return $auditResult return $auditResult
} }
} }

View File

@@ -1,39 +1,53 @@
function Test-MailboxAuditingE3 { function Test-MailboxAuditingE3 {
[CmdletBinding()] [CmdletBinding()]
[OutputType([CISAuditResult])]
param ( param (
# Parameters can be added if needed # Parameters can be added if needed
) )
begin { begin {
<#
Conditions for 6.1.2 (L1) Ensure mailbox auditing for E3 users is Enabled
Validate test for a pass:
- Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
- Specific conditions to check:
- Condition A: Mailbox audit logging is enabled for all user mailboxes.
- Condition B: The `AuditAdmin` actions include `ApplyRecord`, `Create`, `HardDelete`, `MoveToDeletedItems`, `SendAs`, `SendOnBehalf`, `SoftDelete`, `Update`, `UpdateCalendarDelegation`, `UpdateFolderPermissions`, and `UpdateInboxRules`.
- Condition C: The `AuditDelegate` actions include `ApplyRecord`, `Create`, `HardDelete`, `MoveToDeletedItems`, `SendAs`, `SendOnBehalf`, `SoftDelete`, `Update`, `UpdateFolderPermissions`, and `UpdateInboxRules`.
- Condition D: The `AuditOwner` actions include `ApplyRecord`, `HardDelete`, `MoveToDeletedItems`, `SoftDelete`, `Update`, `UpdateCalendarDelegation`, `UpdateFolderPermissions`, and `UpdateInboxRules`.
Validate test for a fail:
- Confirm that the failure conditions in the automated test are consistent with the manual audit results.
- Specific conditions to check:
- Condition A: Mailbox audit logging is not enabled for all user mailboxes.
- Condition B: The `AuditAdmin` actions do not include `ApplyRecord`, `Create`, `HardDelete`, `MoveToDeletedItems`, `SendAs`, `SendOnBehalf`, `SoftDelete`, `Update`, `UpdateCalendarDelegation`, `UpdateFolderPermissions`, and `UpdateInboxRules`.
- Condition C: The `AuditDelegate` actions do not include `ApplyRecord`, `Create`, `HardDelete`, `MoveToDeletedItems`, `SendAs`, `SendOnBehalf`, `SoftDelete`, `Update`, `UpdateFolderPermissions`, and `UpdateInboxRules`.
- Condition D: The `AuditOwner` actions do not include `ApplyRecord`, `HardDelete`, `MoveToDeletedItems`, `SoftDelete`, `Update`, `UpdateCalendarDelegation`, `UpdateFolderPermissions`, and `UpdateInboxRules`.
#>
# Dot source the class script if necessary
#. .\source\Classes\CISAuditResult.ps1
$e3SkuPartNumbers = @("ENTERPRISEPACK", "OFFICESUBSCRIPTION") $e3SkuPartNumbers = @("ENTERPRISEPACK", "OFFICESUBSCRIPTION")
$AdminActions = @("ApplyRecord", "Copy", "Create", "FolderBind", "HardDelete", "Move", "MoveToDeletedItems", "SendAs", "SendOnBehalf", "SoftDelete", "Update", "UpdateCalendarDelegation", "UpdateFolderPermissions", "UpdateInboxRules") $AdminActions = @("ApplyRecord", "Copy", "Create", "FolderBind", "HardDelete", "Move", "MoveToDeletedItems", "SendAs", "SendOnBehalf", "SoftDelete", "Update", "UpdateCalendarDelegation", "UpdateFolderPermissions", "UpdateInboxRules")
$DelegateActions = @("ApplyRecord", "Create", "FolderBind", "HardDelete", "Move", "MoveToDeletedItems", "SendAs", "SendOnBehalf", "SoftDelete", "Update", "UpdateFolderPermissions", "UpdateInboxRules") $DelegateActions = @("ApplyRecord", "Create", "FolderBind", "HardDelete", "Move", "MoveToDeletedItems", "SendAs", "SendOnBehalf", "SoftDelete", "Update", "UpdateFolderPermissions", "UpdateInboxRules")
$OwnerActions = @("ApplyRecord", "Create", "HardDelete", "MailboxLogin", "Move", "MoveToDeletedItems", "SoftDelete", "Update", "UpdateCalendarDelegation", "UpdateFolderPermissions", "UpdateInboxRules") $OwnerActions = @("ApplyRecord", "Create", "HardDelete", "MailboxLogin", "Move", "MoveToDeletedItems", "SoftDelete", "Update", "UpdateCalendarDelegation", "UpdateFolderPermissions", "UpdateInboxRules")
$auditResult = [CISAuditResult]::new()
$auditResult.ELevel = "E3"
$auditResult.ProfileLevel = "L1"
$auditResult.Rec = "6.1.2"
$auditResult.RecDescription = "Ensure mailbox auditing for Office E3 users is Enabled"
$auditResult.CISControlVer = "v8"
$auditResult.CISControl = "8.2"
$auditResult.CISDescription = "Collect audit logs."
$auditResult.IG1 = $true
$auditResult.IG2 = $true
$auditResult.IG3 = $true
$allFailures = @() $allFailures = @()
$allUsers = Get-AzureADUser -All $true $allUsers = Get-AzureADUser -All $true
$processedUsers = @{} # Dictionary to track processed users $processedUsers = @{} # Dictionary to track processed users
$recnum = "6.1.2"
} }
process { process {
foreach ($user in $allUsers) { try {
if ($processedUsers.ContainsKey($user.UserPrincipalName)) { foreach ($user in $allUsers) {
Write-Verbose "Skipping already processed user: $($user.UserPrincipalName)" if ($processedUsers.ContainsKey($user.UserPrincipalName)) {
continue Write-Verbose "Skipping already processed user: $($user.UserPrincipalName)"
} continue
try { }
$licenseDetails = Get-MgUserLicenseDetail -UserId $user.UserPrincipalName $licenseDetails = Get-MgUserLicenseDetail -UserId $user.UserPrincipalName
$hasOfficeE3 = ($licenseDetails | Where-Object { $_.SkuPartNumber -in $e3SkuPartNumbers }).Count -gt 0 $hasOfficeE3 = ($licenseDetails | Where-Object { $_.SkuPartNumber -in $e3SkuPartNumbers }).Count -gt 0
Write-Verbose "Evaluating user $($user.UserPrincipalName) for Office E3 license." Write-Verbose "Evaluating user $($user.UserPrincipalName) for Office E3 license."
@@ -45,67 +59,75 @@ function Test-MailboxAuditingE3 {
$missingActions = @() $missingActions = @()
if ($mailbox.AuditEnabled) { if ($mailbox.AuditEnabled) {
foreach ($action in $AdminActions) { foreach ($action in $AdminActions) {
# Condition B: Checking if the `AuditAdmin` actions include required actions
if ($mailbox.AuditAdmin -notcontains $action) { $missingActions += "Admin action '$action' missing" } if ($mailbox.AuditAdmin -notcontains $action) { $missingActions += "Admin action '$action' missing" }
} }
foreach ($action in $DelegateActions) { foreach ($action in $DelegateActions) {
# Condition C: Checking if the `AuditDelegate` actions include required actions
if ($mailbox.AuditDelegate -notcontains $action) { $missingActions += "Delegate action '$action' missing" } if ($mailbox.AuditDelegate -notcontains $action) { $missingActions += "Delegate action '$action' missing" }
} }
foreach ($action in $OwnerActions) { foreach ($action in $OwnerActions) {
# Condition D: Checking if the `AuditOwner` actions include required actions
if ($mailbox.AuditOwner -notcontains $action) { $missingActions += "Owner action '$action' missing" } if ($mailbox.AuditOwner -notcontains $action) { $missingActions += "Owner action '$action' missing" }
} }
if ($missingActions.Count -gt 0) {
$formattedActions = Format-MissingAction -missingActions $missingActions
$allFailures += "$userUPN|True|$($formattedActions.Admin)|$($formattedActions.Delegate)|$($formattedActions.Owner)"
}
} }
else { else {
$allFailures += "$userUPN`: AuditEnabled - False" # Condition A: Checking if mailbox audit logging is enabled
continue $allFailures += "$userUPN|False|||"
} }
if ($missingActions) {
$formattedActions = Format-MissingActions $missingActions
$allFailures += "$userUPN`: AuditEnabled - True; $formattedActions"
}
# Mark the user as processed # Mark the user as processed
$processedUsers[$user.UserPrincipalName] = $true $processedUsers[$user.UserPrincipalName] = $true
} }
} }
catch {
Write-Warning "Could not retrieve license details for user $($user.UserPrincipalName): $_"
}
}
$auditResult.Result = $allFailures.Count -eq 0 # Prepare failure reasons and details based on compliance
$auditResult.Status = if ($auditResult.Result) { "Pass" } else { "Fail" } $failureReasons = if ($allFailures.Count -eq 0) { "N/A" } else { "Audit issues detected." }
$auditResult.Details = if ($auditResult.Result) { "All Office E3 users have correct mailbox audit settings." } else { $allFailures -join " | " } $details = if ($allFailures.Count -eq 0) {
$auditResult.FailureReason = if (-not $auditResult.Result) { "Audit issues detected." } else { "N/A" } "All Office E3 users have correct mailbox audit settings."
}
else {
"UserPrincipalName|AuditEnabled|AdminActionsMissing|DelegateActionsMissing|OwnerActionsMissing`n" + ($allFailures -join "`n")
}
# Populate the audit result
$params = @{
Rec = $recnum
Result = $allFailures.Count -eq 0
Status = if ($allFailures.Count -eq 0) { "Pass" } else { "Fail" }
Details = $details
FailureReason = $failureReasons
}
$auditResult = Initialize-CISAuditResult @params
}
catch {
Write-Error "An error occurred during the test: $_"
# Retrieve the description from the test definitions
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
} }
end { end {
#$verbosePreference = 'Continue'
$detailsLength = $details.Length
Write-Verbose "Character count of the details: $detailsLength"
if ($detailsLength -gt 32767) {
Write-Verbose "Warning: The character count exceeds the limit for Excel cells."
}
#$verbosePreference = 'SilentlyContinue'
return $auditResult return $auditResult
} }
} }
function Format-MissingActions {
param ([array]$missingActions)
$actionGroups = @{
"Admin" = @()
"Delegate" = @()
"Owner" = @()
}
foreach ($action in $missingActions) {
if ($action -match "(Admin|Delegate|Owner) action '([^']+)' missing") {
$type = $matches[1]
$actionName = $matches[2]
$actionGroups[$type] += $actionName
}
}
$formattedResults = @()
foreach ($type in $actionGroups.Keys) {
if ($actionGroups[$type].Count -gt 0) {
$formattedResults += "$($type) actions missing: $($actionGroups[$type] -join ', ')"
}
}
return $formattedResults -join '; '
}

View File

@@ -1,120 +1,130 @@
function Test-MailboxAuditingE5 { function Test-MailboxAuditingE5 {
[CmdletBinding()] [CmdletBinding()]
param () [OutputType([CISAuditResult])]
param (
# Parameters can be added if needed
)
begin { begin {
# Dot source the class script if necessary
#. .\source\Classes\CISAuditResult.ps1
# Conditions for 6.1.3 (L1) Ensure mailbox auditing for E5 users is Enabled
#
# Validate test for a pass:
# - Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
# - Specific conditions to check:
# - Condition A: Mailbox auditing is enabled for E5 users.
# - Condition B: AuditAdmin actions include ApplyRecord, Create, HardDelete, MailItemsAccessed, MoveToDeletedItems, Send, SendAs, SendOnBehalf, SoftDelete, Update, UpdateCalendarDelegation, UpdateFolderPermissions, UpdateInboxRules.
# - Condition C: AuditDelegate actions include ApplyRecord, Create, HardDelete, MailItemsAccessed, MoveToDeletedItems, SendAs, SendOnBehalf, SoftDelete, Update, UpdateFolderPermissions, UpdateInboxRules.
# - Condition D: AuditOwner actions include ApplyRecord, HardDelete, MailItemsAccessed, MoveToDeletedItems, Send, SoftDelete, Update, UpdateCalendarDelegation, UpdateFolderPermissions, UpdateInboxRules.
#
# Validate test for a fail:
# - Confirm that the failure conditions in the automated test are consistent with the manual audit results.
# - Specific conditions to check:
# - Condition A: Mailbox auditing is not enabled for E5 users.
# - Condition B: AuditAdmin actions do not include all of the following: ApplyRecord, Create, HardDelete, MailItemsAccessed, MoveToDeletedItems, Send, SendAs, SendOnBehalf, SoftDelete, Update, UpdateCalendarDelegation, UpdateFolderPermissions, UpdateInboxRules.
# - Condition C: AuditDelegate actions do not include all of the following: ApplyRecord, Create, HardDelete, MailItemsAccessed, MoveToDeletedItems, SendAs, SendOnBehalf, SoftDelete, Update, UpdateFolderPermissions, UpdateInboxRules.
# - Condition D: AuditOwner actions do not include all of the following: ApplyRecord, HardDelete, MailItemsAccessed, MoveToDeletedItems, Send, SoftDelete, Update, UpdateCalendarDelegation, UpdateFolderPermissions, UpdateInboxRules.
$e5SkuPartNumbers = @("SPE_E5", "ENTERPRISEPREMIUM", "OFFICEE5")
$AdminActions = @("ApplyRecord", "Copy", "Create", "FolderBind", "HardDelete", "MailItemsAccessed", "Move", "MoveToDeletedItems", "SendAs", "SendOnBehalf", "Send", "SoftDelete", "Update", "UpdateCalendarDelegation", "UpdateFolderPermissions", "UpdateInboxRules") $AdminActions = @("ApplyRecord", "Copy", "Create", "FolderBind", "HardDelete", "MailItemsAccessed", "Move", "MoveToDeletedItems", "SendAs", "SendOnBehalf", "Send", "SoftDelete", "Update", "UpdateCalendarDelegation", "UpdateFolderPermissions", "UpdateInboxRules")
$DelegateActions = @("ApplyRecord", "Create", "FolderBind", "HardDelete", "MailItemsAccessed", "Move", "MoveToDeletedItems", "SendAs", "SendOnBehalf", "SoftDelete", "Update", "UpdateFolderPermissions", "UpdateInboxRules") $DelegateActions = @("ApplyRecord", "Create", "FolderBind", "HardDelete", "MailItemsAccessed", "Move", "MoveToDeletedItems", "SendAs", "SendOnBehalf", "SoftDelete", "Update", "UpdateFolderPermissions", "UpdateInboxRules")
$OwnerActions = @("ApplyRecord", "Create", "HardDelete", "MailboxLogin", "Move", "MailItemsAccessed", "MoveToDeletedItems", "Send", "SoftDelete", "Update", "UpdateCalendarDelegation", "UpdateFolderPermissions", "UpdateInboxRules") $OwnerActions = @("ApplyRecord", "Create", "HardDelete", "MailboxLogin", "Move", "MailItemsAccessed", "MoveToDeletedItems", "Send", "SoftDelete", "Update", "UpdateCalendarDelegation", "UpdateFolderPermissions", "UpdateInboxRules")
$auditResult = [CISAuditResult]::new()
$auditResult.ELevel = "E5"
$auditResult.ProfileLevel = "L1"
$auditResult.Rec = "6.1.3"
$auditResult.RecDescription = "Ensure mailbox auditing for Office E5 users is Enabled"
$auditResult.CISControlVer = "v8"
$auditResult.CISControl = "8.2"
$auditResult.CISDescription = "Collect audit logs."
$auditResult.IG1 = $true
$auditResult.IG2 = $true
$auditResult.IG3 = $true
$allFailures = @() $allFailures = @()
$allUsers = Get-AzureADUser -All $true $allUsers = Get-AzureADUser -All $true
$processedUsers = @{} # Dictionary to track processed users $processedUsers = @{} # Dictionary to track processed users
$recnum = "6.1.3"
} }
process { process {
foreach ($user in $allUsers) { try {
if ($processedUsers.ContainsKey($user.UserPrincipalName)) { foreach ($user in $allUsers) {
continue if ($processedUsers.ContainsKey($user.UserPrincipalName)) {
} Write-Verbose "Skipping already processed user: $($user.UserPrincipalName)"
continue
}
try {
# Define SKU Part Numbers for Office E5 licenses
# Define SKU Part Numbers for Office E5 licenses
$e5SkuPartNumbers = @("SPE_E5", "ENTERPRISEPREMIUM", "OFFICEE5")
$licenseDetails = Get-MgUserLicenseDetail -UserId $user.UserPrincipalName $licenseDetails = Get-MgUserLicenseDetail -UserId $user.UserPrincipalName
$hasOfficeE5 = ($licenseDetails | Where-Object { $_.SkuPartNumber -in $e5SkuPartNumbers }).Count -gt 0 $hasOfficeE5 = ($licenseDetails | Where-Object { $_.SkuPartNumber -in $e5SkuPartNumbers }).Count -gt 0
Write-Verbose "Evaluating user $($user.UserPrincipalName) for Office E5 license." Write-Verbose "Evaluating user $($user.UserPrincipalName) for Office E5 license."
if ($hasOfficeE5) { if ($hasOfficeE5) {
$userUPN = $user.UserPrincipalName $userUPN = $user.UserPrincipalName
$mailbox = Get-EXOMailbox -Identity $userUPN -PropertySets Audit $mailbox = Get-EXOMailbox -Identity $userUPN -PropertySets Audit
$missingActions = @() $missingActions = @()
if ($mailbox.AuditEnabled) { if ($mailbox.AuditEnabled) {
# Validate Admin actions
foreach ($action in $AdminActions) { foreach ($action in $AdminActions) {
if ($mailbox.AuditAdmin -notcontains $action) { $missingActions += "Admin action '$action' missing" } if ($mailbox.AuditAdmin -notcontains $action) { $missingActions += "Admin action '$action' missing" } # Condition B
} }
# Validate Delegate actions
foreach ($action in $DelegateActions) { foreach ($action in $DelegateActions) {
if ($mailbox.AuditDelegate -notcontains $action) { $missingActions += "Delegate action '$action' missing" } if ($mailbox.AuditDelegate -notcontains $action) { $missingActions += "Delegate action '$action' missing" } # Condition C
} }
# Validate Owner actions
foreach ($action in $OwnerActions) { foreach ($action in $OwnerActions) {
if ($mailbox.AuditOwner -notcontains $action) { $missingActions += "Owner action '$action' missing" } if ($mailbox.AuditOwner -notcontains $action) { $missingActions += "Owner action '$action' missing" } # Condition D
}
if ($missingActions.Count -gt 0) {
$formattedActions = Format-MissingAction -missingActions $missingActions
$allFailures += "$userUPN|True|$($formattedActions.Admin)|$($formattedActions.Delegate)|$($formattedActions.Owner)"
} }
} }
else { else {
$allFailures += "$userUPN`: AuditEnabled - False" $allFailures += "$userUPN|False|||"
continue
} }
if ($missingActions) { # Mark the user as processed
$formattedActions = Format-MissingActions $missingActions
$allFailures += "$userUPN`: AuditEnabled - True; $formattedActions"
}
else {
Write-Verbose "User $($user.UserPrincipalName) passed the mailbox audit checks."
}
$processedUsers[$user.UserPrincipalName] = $true $processedUsers[$user.UserPrincipalName] = $true
} }
else {
# Adding verbose output to indicate the user does not have an E5 license
Write-Verbose "User $($user.UserPrincipalName) does not have an Office E5 license."
}
} }
catch {
Write-Warning "Could not retrieve license details for user $($user.UserPrincipalName): $_"
}
}
if ($allFailures.Count -eq 0) { # Prepare failure reasons and details based on compliance
Write-Verbose "All evaluated E5 users have correct mailbox audit settings." $failureReasons = if ($allFailures.Count -eq 0) { "N/A" } else { "Audit issues detected." }
$details = if ($allFailures.Count -eq 0) {
"All Office E5 users have correct mailbox audit settings." # Condition A for pass
}
else {
"UserPrincipalName|AuditEnabled|AdminActionsMissing|DelegateActionsMissing|OwnerActionsMissing`n" + ($allFailures -join "`n") # Condition A for fail
}
# Populate the audit result
$params = @{
Rec = $recnum
Result = $allFailures.Count -eq 0
Status = if ($allFailures.Count -eq 0) { "Pass" } else { "Fail" }
Details = $details
FailureReason = $failureReasons
}
$auditResult = Initialize-CISAuditResult @params
}
catch {
Write-Error "An error occurred during the test: $_"
# Retrieve the description from the test definitions
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
} }
$auditResult.Result = $allFailures.Count -eq 0
$auditResult.Status = if ($auditResult.Result) { "Pass" } else { "Fail" }
$auditResult.Details = if ($auditResult.Result) { "All Office E5 users have correct mailbox audit settings." } else { $allFailures -join " | " }
$auditResult.FailureReason = if (-not $auditResult.Result) { "Audit issues detected." } else { "N/A" }
} }
end { end {
#$verbosePreference = 'Continue'
$detailsLength = $details.Length
Write-Verbose "Character count of the details: $detailsLength"
if ($detailsLength -gt 32767) {
Write-Verbose "Warning: The character count exceeds the limit for Excel cells."
}
#$verbosePreference = 'SilentlyContinue'
return $auditResult return $auditResult
} }
} }
function Format-MissingActions {
param ([array]$missingActions)
$actionGroups = @{
"Admin" = @()
"Delegate" = @()
"Owner" = @()
}
foreach ($action in $missingActions) {
if ($action -match "(Admin|Delegate|Owner) action '([^']+)' missing") {
$type = $matches[1]
$actionName = $matches[2]
$actionGroups[$type] += $actionName
}
}
$formattedResults = @()
foreach ($type in $actionGroups.Keys) {
if ($actionGroups[$type].Count -gt 0) {
$formattedResults += "$($type) actions missing: $($actionGroups[$type] -join ', ')"
}
}
return $formattedResults -join '; '
}

View File

@@ -1,54 +1,80 @@
function Test-ManagedApprovedPublicGroups { function Test-ManagedApprovedPublicGroups {
[CmdletBinding()] [CmdletBinding()]
[OutputType([CISAuditResult])]
param ( param (
# Define your parameters here # Parameters can be added if needed
) )
begin { begin {
# Dot source the class script # Dot source the class script if necessary
#. .\source\Classes\CISAuditResult.ps1
$auditResults = @() # Initialization code, if needed
$recnum = "1.2.1"
# Conditions for 1.2.1 (L2) Ensure that only organizationally managed/approved public groups exist (Automated)
#
# Validate test for a pass:
# - Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
# - Specific conditions to check:
# - Condition A: No groups have the status 'Public' in the privacy column on the Active teams and groups page.
# - Condition B: Using Microsoft Graph PowerShell, all groups return a status other than 'Public' when checked.
#
# Validate test for a fail:
# - Confirm that the failure conditions in the automated test are consistent with the manual audit results.
# - Specific conditions to check:
# - Condition A: One or more groups have the status 'Public' in the privacy column on the Active teams and groups page.
# - Condition B: Using Microsoft Graph PowerShell, one or more groups return a status of 'Public' when checked.
} }
process { process {
# 1.2.1 (L2) Ensure that only organizationally managed/approved public groups exist (Automated) try {
# Step: Retrieve all groups with visibility set to 'Public'
$allGroups = Get-MgGroup -All | Where-Object { $_.Visibility -eq "Public" } | Select-Object DisplayName, Visibility
$allGroups = Get-MgGroup -All | Where-Object { $_.Visibility -eq "Public" } | Select-Object DisplayName, Visibility # Step: Determine failure reasons based on the presence of public groups
$failureReasons = if ($null -ne $allGroups -and $allGroups.Count -gt 0) {
"There are public groups present that are not organizationally managed/approved."
}
else {
"N/A"
}
# Check if there are public groups and if they are organizationally managed/approved # Step: Prepare details for the audit result
$auditResult = [CISAuditResult]::new() $details = if ($null -eq $allGroups -or $allGroups.Count -eq 0) {
$auditResult.CISControlVer = "v8" "No public groups found."
$auditResult.CISControl = "3.3" }
$auditResult.CISDescription = "Configure Data Access Control Lists" else {
$auditResult.Rec = "1.2.1" $groupDetails = $allGroups | ForEach-Object { $_.DisplayName + " (" + $_.Visibility + ")" }
$auditResult.ELevel = "E3" "Public groups found: $($groupDetails -join ', ')"
$auditResult.ProfileLevel = "L2" }
$auditResult.IG1 = $true
$auditResult.IG2 = $true
$auditResult.IG3 = $true # Based on the provided CIS Control image, IG3 is not applicable
$auditResult.RecDescription = "Ensure that only organizationally managed/approved public groups exist"
if ($null -eq $allGroups -or $allGroups.Count -eq 0) { # Step: Create and populate the CISAuditResult object
$auditResult.Result = $true $params = @{
$auditResult.Details = "No public groups found." Rec = $recnum
$auditResult.FailureReason = "N/A" Result = $null -eq $allGroups -or $allGroups.Count -eq 0
$auditResult.Status = "Pass" Status = if ($null -eq $allGroups -or $allGroups.Count -eq 0) { "Pass" } else { "Fail" }
Details = $details
FailureReason = $failureReasons
}
$auditResult = Initialize-CISAuditResult @params
} }
else { catch {
$groupDetails = $allGroups | ForEach-Object { $_.DisplayName + " (" + $_.Visibility + ")" } Write-Error "An error occurred during the test: $_"
$detailsString = $groupDetails -join ', '
$auditResult.Result = $false # Retrieve the description from the test definitions
$auditResult.Details = "Public groups found: $detailsString" $testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$auditResult.FailureReason = "There are public groups present that are not organizationally managed/approved." $description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$auditResult.Status = "Fail"
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
} }
$auditResults += $auditResult
} }
end { end {
# Return auditResults # Return the audit result
return $auditResults return $auditResult
} }
} }

View File

@@ -1,46 +1,77 @@
function Test-MeetingChatNoAnonymous { function Test-MeetingChatNoAnonymous {
[CmdletBinding()] [CmdletBinding()]
[OutputType([CISAuditResult])]
param ( param (
# Aligned
# Parameters can be defined here if needed # Parameters can be defined here if needed
) )
begin { begin {
# Dot source the class script # Dot source the class script if necessary
#. .\source\Classes\CISAuditResult.ps1
$auditResults = @() # Initialization code, if needed
$recnum = "8.5.5"
} }
process { process {
# 8.5.5 (L2) Ensure meeting chat does not allow anonymous users try {
# Name doesn't match profile level in benchmarks either. # 8.5.5 (L2) Ensure meeting chat does not allow anonymous users
#
# Validate test for a pass:
# - Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
# - Specific conditions to check:
# - Condition A: The `MeetingChatEnabledType` setting in Teams is set to `EnabledExceptAnonymous`.
# - Condition B: The setting for meeting chat is configured to allow chat for everyone except anonymous users.
# - Condition C: Verification using the Teams Admin Center confirms that the meeting chat settings are configured as recommended.
#
# Validate test for a fail:
# - Confirm that the failure conditions in the automated test are consistent with the manual audit results.
# - Specific conditions to check:
# - Condition A: The `MeetingChatEnabledType` setting in Teams is not set to `EnabledExceptAnonymous`.
# - Condition B: The setting for meeting chat allows chat for anonymous users.
# - Condition C: Verification using the Teams Admin Center indicates that the meeting chat settings are not configured as recommended.
# Connect to Teams PowerShell using Connect-MicrosoftTeams # Retrieve the Teams meeting policy for meeting chat
$CsTeamsMeetingPolicyChat = Get-CsTeamsMeetingPolicy -Identity Global | Select-Object -Property MeetingChatEnabledType
# Condition A: Check if the MeetingChatEnabledType is set to 'EnabledExceptAnonymous'
$chatAnonDisabled = $CsTeamsMeetingPolicyChat.MeetingChatEnabledType -eq 'EnabledExceptAnonymous'
$CsTeamsMeetingPolicyChat = Get-CsTeamsMeetingPolicy -Identity Global | Select-Object -Property MeetingChatEnabledType # Prepare failure reasons and details based on compliance
$chatAnonDisabled = $CsTeamsMeetingPolicyChat.MeetingChatEnabledType -eq 'EnabledExceptAnonymous' $failureReasons = if ($chatAnonDisabled) {
"N/A"
}
else {
"Meeting chat allows anonymous users"
}
# Create an instance of CISAuditResult and populate it $details = "MeetingChatEnabledType is set to $($CsTeamsMeetingPolicyChat.MeetingChatEnabledType)"
$auditResult = [CISAuditResult]::new()
$auditResult.CISControlVer = "v8"
$auditResult.CISControl = "0.0" # Explicitly Not Mapped as per the image provided
$auditResult.CISDescription = "Explicitly Not Mapped"
$auditResult.Rec = "8.5.5"
$auditResult.ELevel = "E3"
$auditResult.ProfileLevel = "L1"
$auditResult.IG1 = $false # Set based on the CIS Controls image
$auditResult.IG2 = $false # Set based on the CIS Controls image
$auditResult.IG3 = $false # Set based on the CIS Controls image
$auditResult.RecDescription = "Ensure meeting chat does not allow anonymous users"
$auditResult.Result = $chatAnonDisabled
$auditResult.Details = "MeetingChatEnabledType is set to $($CsTeamsMeetingPolicyChat.MeetingChatEnabledType)"
$auditResult.FailureReason = if ($chatAnonDisabled) { "N/A" } else { "Meeting chat allows anonymous users" }
$auditResult.Status = if ($chatAnonDisabled) { "Pass" } else { "Fail" }
$auditResults += $auditResult # Create and populate the CISAuditResult object
$params = @{
Rec = $recnum
Result = $chatAnonDisabled
Status = if ($chatAnonDisabled) { "Pass" } else { "Fail" }
Details = $details
FailureReason = $failureReasons
}
$auditResult = Initialize-CISAuditResult @params
}
catch {
Write-Error "An error occurred during the test: $_"
# Retrieve the description from the test definitions
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
} }
end { end {
# Return auditResults # Return the audit result
return $auditResults return $auditResult
} }
} }

View File

@@ -1,54 +1,82 @@
function Test-ModernAuthExchangeOnline { function Test-ModernAuthExchangeOnline {
[CmdletBinding()] [CmdletBinding()]
[OutputType([CISAuditResult])]
param ( param (
# Aligned
# Define your parameters here # Define your parameters here
) )
begin { begin {
# Dot source the class script if necessary
#. .\source\Classes\CISAuditResult.ps1
$auditResults = [CISAuditResult]::new() # Conditions for 6.5.1 (L1) Ensure modern authentication for Exchange Online is enabled
# Initialization code #
# Validate test for a pass:
# - Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
# - Specific conditions to check:
# - Condition A: Modern authentication for Exchange Online is enabled.
# - Condition B: Exchange Online clients use modern authentication to log in to Microsoft 365 mailboxes.
# - Condition C: Users of older email clients, such as Outlook 2013 and Outlook 2016, are no longer able to authenticate to Exchange using Basic Authentication.
#
# Validate test for a fail:
# - Confirm that the failure conditions in the automated test are consistent with the manual audit results.
# - Specific conditions to check:
# - Condition A: Modern authentication for Exchange Online is not enabled.
# - Condition B: Exchange Online clients do not use modern authentication to log in to Microsoft 365 mailboxes.
# - Condition C: Users of older email clients, such as Outlook 2013 and Outlook 2016, are still able to authenticate to Exchange using Basic Authentication.
$recnum = "6.5.1"
} }
process { process {
try { try {
# Ensuring the ExchangeOnlineManagement module is available # Ensuring the ExchangeOnlineManagement module is available
# 6.5.1 (L1) Ensure modern authentication for Exchange Online is enabled # 6.5.1 (L1) Ensure modern authentication for Exchange Online is enabled
# Check modern authentication setting in Exchange Online configuration (Condition A and B)
$orgConfig = Get-OrganizationConfig | Select-Object -Property Name, OAuth2ClientProfileEnabled $orgConfig = Get-OrganizationConfig | Select-Object -Property Name, OAuth2ClientProfileEnabled
# Create an instance of CISAuditResult and populate it # Prepare failure reasons and details based on compliance
$failureReasons = if (-not $orgConfig.OAuth2ClientProfileEnabled) {
"Modern authentication is disabled"
}
else {
"N/A"
}
$auditResults.CISControlVer = "v8" # Details include the current setting (Condition A and B)
$auditResults.CISControl = "3.10" $details = "OAuth2ClientProfileEnabled: $($orgConfig.OAuth2ClientProfileEnabled) for Organization: $($orgConfig.Name)"
$auditResults.CISDescription = "Encrypt Sensitive Data in Transit"
$auditResults.IG1 = $false # As per CIS Control v8 mapping for IG1
$auditResults.IG2 = $true # As per CIS Control v8 mapping for IG2
$auditResults.IG3 = $true # As per CIS Control v8 mapping for IG3
$auditResults.ELevel = "E3" # Based on your environment (E3, E5, etc.)
$auditResults.Profile = "L1"
$auditResults.Rec = "6.5.1"
$auditResults.RecDescription = "Ensure modern authentication for Exchange Online is enabled (Automated)"
$auditResults.Result = $orgConfig.OAuth2ClientProfileEnabled
$auditResults.Details = $auditResults.Details = $orgConfig.Name + " OAuth2ClientProfileEnabled: " + $orgConfig.OAuth2ClientProfileEnabled
$auditResults.FailureReason = if (-not $orgConfig.OAuth2ClientProfileEnabled) { "Modern authentication is disabled" } else { "N/A" }
$auditResults.Status = if ($orgConfig.OAuth2ClientProfileEnabled) { "Pass" } else { "Fail" }
# Create and populate the CISAuditResult object
$params = @{
Rec = $recnum
Result = $orgConfig.OAuth2ClientProfileEnabled
Status = if ($orgConfig.OAuth2ClientProfileEnabled) { "Pass" } else { "Fail" }
Details = $details
FailureReason = $failureReasons
}
$auditResult = Initialize-CISAuditResult @params
} }
catch { catch {
Write-Error "An error occurred while testing modern authentication for Exchange Online: $_" Write-Error "An error occurred during the test: $_"
# Retrieve the description from the test definitions
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
} }
} }
end { end {
# Return auditResults # Return the audit result
return $auditResults return $auditResult
} }
} }

View File

@@ -1,39 +1,77 @@
function Test-ModernAuthSharePoint { function Test-ModernAuthSharePoint {
[CmdletBinding()] [CmdletBinding()]
[OutputType([CISAuditResult])]
param ( param (
# Aligned
# Define your parameters here # Define your parameters here
) )
begin { begin {
# Initialization code # Dot source the class script if necessary
#. .\source\Classes\CISAuditResult.ps1
$auditResult = [CISAuditResult]::new() # Initialization code, if needed
$recnum = "7.2.1"
<#
# Conditions for 7.2.1 (L1) Ensure modern authentication for SharePoint applications is required
## Validate test for a pass:
# - Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
# - Specific conditions to check:
# - Condition A: The setting "Apps that don't use modern authentication" is set to "Block access" in the SharePoint admin center.
# - Condition B: The PowerShell command `Get-SPOTenant | ft LegacyAuthProtocolsEnabled` returns `False`.
## Validate test for a fail:
# - Confirm that the failure conditions in the automated test are consistent with the manual audit results.
# - Specific conditions to check:
# - Condition A: The setting "Apps that don't use modern authentication" is not set to "Block access" in the SharePoint admin center.
# - Condition B: The PowerShell command `Get-SPOTenant | ft LegacyAuthProtocolsEnabled` returns `True`.
#>
} }
process { process {
# 7.2.1 (L1) Ensure modern authentication for SharePoint applications is required try {
$SPOTenant = Get-SPOTenant | Select-Object -Property LegacyAuthProtocolsEnabled # 7.2.1 (L1) Ensure modern authentication for SharePoint applications is required
$modernAuthForSPRequired = -not $SPOTenant.LegacyAuthProtocolsEnabled $SPOTenant = Get-SPOTenant | Select-Object -Property LegacyAuthProtocolsEnabled
$modernAuthForSPRequired = -not $SPOTenant.LegacyAuthProtocolsEnabled
# Populate the auditResult object with the required properties # Prepare failure reasons and details based on compliance
$auditResult.CISControlVer = "v8" $failureReasons = if (-not $modernAuthForSPRequired) {
$auditResult.CISControl = "3.10" "Legacy authentication protocols are enabled" # Fail Condition B
$auditResult.CISDescription = "Encrypt Sensitive Data in Transit" }
$auditResult.Rec = "7.2.1" else {
$auditResult.ELevel = "E3" "N/A"
$auditResult.ProfileLevel = "L1" }
$auditResult.IG1 = $false
$auditResult.IG2 = $true $details = "LegacyAuthProtocolsEnabled: $($SPOTenant.LegacyAuthProtocolsEnabled)" # Details for Condition B
$auditResult.IG3 = $true
$auditResult.RecDescription = "Modern Authentication for SharePoint Applications" # Create and populate the CISAuditResult object
$auditResult.Result = $modernAuthForSPRequired $params = @{
$auditResult.Details = "LegacyAuthProtocolsEnabled: $($SPOTenant.LegacyAuthProtocolsEnabled)" Rec = $recnum
$auditResult.FailureReason = if (-not $modernAuthForSPRequired) { "Legacy authentication protocols are enabled" } else { "N/A" } Result = $modernAuthForSPRequired
$auditResult.Status = if ($modernAuthForSPRequired) { "Pass" } else { "Fail" } Status = if ($modernAuthForSPRequired) { "Pass" } else { "Fail" }
Details = $details
FailureReason = $failureReasons
}
$auditResult = Initialize-CISAuditResult @params
}
catch {
Write-Error "An error occurred during the test: $_"
# Retrieve the description from the test definitions
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
} }
end { end {
# Return auditResult # Return the audit result
return $auditResult return $auditResult
} }
} }

View File

@@ -1,45 +1,77 @@
function Test-NoAnonymousMeetingJoin { function Test-NoAnonymousMeetingJoin {
[CmdletBinding()] [CmdletBinding()]
[OutputType([CISAuditResult])]
param ( param (
# Aligned
# Parameters can be defined here if needed # Parameters can be defined here if needed
) )
begin { begin {
# Dot source the class script # Dot source the class script if necessary
#. .\source\Classes\CISAuditResult.ps1
$auditResults = @() # Initialization code, if needed
$recnum = "8.5.1"
} }
process { process {
# 8.5.1 (L2) Ensure anonymous users can't join a meeting try {
# 8.5.1 (L2) Ensure anonymous users can't join a meeting
#
# Validate test for a pass:
# - Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
# - Specific conditions to check:
# - Condition A: `AllowAnonymousUsersToJoinMeeting` is set to `False`.
# - Condition B: Verification using the UI confirms that `Anonymous users can join a meeting` is set to `Off` in the Global meeting policy.
# - Condition C: PowerShell command output indicates that anonymous users are not allowed to join meetings.
#
# Validate test for a fail:
# - Confirm that the failure conditions in the automated test are consistent with the manual audit results.
# - Specific conditions to check:
# - Condition A: `AllowAnonymousUsersToJoinMeeting` is not set to `False`.
# - Condition B: Verification using the UI shows that `Anonymous users can join a meeting` is not set to `Off` in the Global meeting policy.
# - Condition C: PowerShell command output indicates that anonymous users are allowed to join meetings.
# Connect to Teams PowerShell using Connect-MicrosoftTeams # Connect to Teams PowerShell using Connect-MicrosoftTeams
$teamsMeetingPolicy = Get-CsTeamsMeetingPolicy -Identity Global $teamsMeetingPolicy = Get-CsTeamsMeetingPolicy -Identity Global
$allowAnonymousUsersToJoinMeeting = $teamsMeetingPolicy.AllowAnonymousUsersToJoinMeeting $allowAnonymousUsersToJoinMeeting = $teamsMeetingPolicy.AllowAnonymousUsersToJoinMeeting
# Create an instance of CISAuditResult and populate it # Prepare failure reasons and details based on compliance
$auditResult = [CISAuditResult]::new() $failureReasons = if ($allowAnonymousUsersToJoinMeeting) {
$auditResult.CISControlVer = "v8" "Anonymous users are allowed to join meetings"
$auditResult.CISControl = "0.0" # The control is Explicitly Not Mapped as per the image provided }
$auditResult.CISDescription = "Explicitly Not Mapped" else {
$auditResult.Rec = "8.5.1" "N/A"
$auditResult.ELevel = "E3" }
$auditResult.ProfileLevel = "L2"
$auditResult.IG1 = $false # Set based on the CIS Controls image
$auditResult.IG2 = $false # Set based on the CIS Controls image
$auditResult.IG3 = $false # Set based on the CIS Controls image
$auditResult.RecDescription = "Ensure anonymous users can't join a meeting"
$auditResult.Result = -not $allowAnonymousUsersToJoinMeeting
$auditResult.Details = "AllowAnonymousUsersToJoinMeeting is set to $allowAnonymousUsersToJoinMeeting"
$auditResult.FailureReason = if ($allowAnonymousUsersToJoinMeeting) { "Anonymous users are allowed to join meetings" } else { "N/A" }
$auditResult.Status = if (-not $allowAnonymousUsersToJoinMeeting) { "Pass" } else { "Fail" }
$auditResults += $auditResult $details = "AllowAnonymousUsersToJoinMeeting is set to $allowAnonymousUsersToJoinMeeting"
# Create and populate the CISAuditResult object
$params = @{
Rec = $recnum
Result = -not $allowAnonymousUsersToJoinMeeting
Status = if (-not $allowAnonymousUsersToJoinMeeting) { "Pass" } else { "Fail" }
Details = $details
FailureReason = $failureReasons
}
$auditResult = Initialize-CISAuditResult @params
}
catch {
Write-Error "An error occurred during the test: $_"
# Retrieve the description from the test definitions
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
} }
end { end {
# Return auditResults # Return the audit result
return $auditResults return $auditResult
} }
} }

View File

@@ -1,45 +1,78 @@
function Test-NoAnonymousMeetingStart { function Test-NoAnonymousMeetingStart {
[CmdletBinding()] [CmdletBinding()]
[OutputType([CISAuditResult])]
param ( param (
# Aligned
# Parameters can be defined here if needed # Parameters can be defined here if needed
) )
begin { begin {
# Dot source the class script # Dot source the class script if necessary
#. .\source\Classes\CISAuditResult.ps1
$auditResults = @() # Initialization code, if needed
$recnum = "8.5.2"
} }
process { process {
# 8.5.2 (L1) Ensure anonymous users and dial-in callers can't start a meeting try {
# 8.5.2 (L1) Ensure anonymous users and dial-in callers can't start a meeting
#
# Validate test for a pass:
# - Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
# - Specific conditions to check:
# - Condition A: The `AllowAnonymousUsersToStartMeeting` setting in the Teams admin center is set to `False`.
# - Condition B: The setting for anonymous users and dial-in callers starting a meeting is configured to ensure they must wait in the lobby.
# - Condition C: Verification using the UI confirms that the setting `Anonymous users and dial-in callers can start a meeting` is set to `Off`.
#
# Validate test for a fail:
# - Confirm that the failure conditions in the automated test are consistent with the manual audit results.
# - Specific conditions to check:
# - Condition A: The `AllowAnonymousUsersToStartMeeting` setting in the Teams admin center is not set to `False`.
# - Condition B: The setting for anonymous users and dial-in callers starting a meeting allows them to bypass the lobby.
# - Condition C: Verification using the UI indicates that the setting `Anonymous users and dial-in callers can start a meeting` is not set to `Off`.
# Connect to Teams PowerShell using Connect-MicrosoftTeams # Connect to Teams PowerShell using Connect-MicrosoftTeams
$CsTeamsMeetingPolicyAnonymous = Get-CsTeamsMeetingPolicy -Identity Global | Select-Object -Property AllowAnonymousUsersToStartMeeting # Retrieve the Teams meeting policy for the global scope and check if anonymous users can start meetings
$anonymousStartDisabled = -not $CsTeamsMeetingPolicyAnonymous.AllowAnonymousUsersToStartMeeting $CsTeamsMeetingPolicyAnonymous = Get-CsTeamsMeetingPolicy -Identity Global | Select-Object -Property AllowAnonymousUsersToStartMeeting
$anonymousStartDisabled = -not $CsTeamsMeetingPolicyAnonymous.AllowAnonymousUsersToStartMeeting
# Create an instance of CISAuditResult and populate it # Prepare failure reasons and details based on compliance
$auditResult = [CISAuditResult]::new() $failureReasons = if ($anonymousStartDisabled) {
$auditResult.CISControlVer = "v8" "N/A"
$auditResult.CISControl = "0.0" # Explicitly Not Mapped as per the image provided }
$auditResult.CISDescription = "Explicitly Not Mapped" else {
$auditResult.Rec = "8.5.2" "Anonymous users and dial-in callers can start a meeting" # Condition A and B
$auditResult.ELevel = "E3" }
$auditResult.ProfileLevel = "L1"
$auditResult.IG1 = $false # Set based on the CIS Controls image
$auditResult.IG2 = $false # Set based on the CIS Controls image
$auditResult.IG3 = $false # Set based on the CIS Controls image
$auditResult.RecDescription = "Ensure anonymous users and dial-in callers can't start a meeting"
$auditResult.Result = $anonymousStartDisabled
$auditResult.Details = "AllowAnonymousUsersToStartMeeting is set to $($CsTeamsMeetingPolicyAnonymous.AllowAnonymousUsersToStartMeeting)"
$auditResult.FailureReason = if ($anonymousStartDisabled) { "N/A" } else { "Anonymous users and dial-in callers can start a meeting" }
$auditResult.Status = if ($anonymousStartDisabled) { "Pass" } else { "Fail" }
$auditResults += $auditResult $details = "AllowAnonymousUsersToStartMeeting is set to $($CsTeamsMeetingPolicyAnonymous.AllowAnonymousUsersToStartMeeting)" # Condition C
# Create and populate the CISAuditResult object
$params = @{
Rec = $recnum
Result = $anonymousStartDisabled
Status = if ($anonymousStartDisabled) { "Pass" } else { "Fail" }
Details = $details
FailureReason = $failureReasons
}
$auditResult = Initialize-CISAuditResult @params
}
catch {
Write-Error "An error occurred during the test: $_"
# Retrieve the description from the test definitions
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
} }
end { end {
# Return auditResults # Return the audit result
return $auditResults return $auditResult
} }
} }

View File

@@ -1,42 +1,84 @@
function Test-NoWhitelistDomains { function Test-NoWhitelistDomains {
[CmdletBinding()] [CmdletBinding()]
[OutputType([CISAuditResult])]
param ( param (
# Aligned
# Define your parameters here # Define your parameters here
) )
begin { begin {
# Initialization code # Dot source the class script if necessary
#. .\source\Classes\CISAuditResult.ps1
$auditResult = [CISAuditResult]::new() # Initialization code, if needed
$recnum = "6.2.2"
<#
Conditions for 6.2.2 (L1) Ensure mail transport rules do not whitelist specific domains (Automated)
Validate test for a pass:
- Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
- Specific conditions to check:
- Condition A: No mail transport rules whitelist any specific domains.
- Condition B: Verification of mail transport rules using PowerShell shows no domains are whitelisted.
- Condition C: Manual review in Exchange Online admin center confirms no whitelisted domains in transport rules.
Validate test for a fail:
- Confirm that the failure conditions in the automated test are consistent with the manual audit results.
- Specific conditions to check:
- Condition A: One or more mail transport rules whitelist specific domains.
- Condition B: Verification of mail transport rules using PowerShell shows one or more domains are whitelisted.
- Condition C: Manual review in Exchange Online admin center shows whitelisted domains in transport rules.
#>
} }
process { process {
# 6.2.2 (L1) Ensure mail transport rules do not whitelist specific domains try {
$whitelistedRules = Get-TransportRule | Where-Object { $_.SetSCL -eq -1 -and $_.SenderDomainIs -ne $null } # 6.2.2 (L1) Ensure mail transport rules do not whitelist specific domains
$auditResult.CISControl = "0.0" # Retrieve transport rules that whitelist specific domains
$auditResult.CISControlVer = "v8" # Condition A: Checking for transport rules that whitelist specific domains
$auditResult.CISDescription = "Explicitly Not Mapped" $whitelistedRules = Get-TransportRule | Where-Object { $_.SetSCL -eq -1 -and $null -ne $_.SenderDomainIs }
$auditResult.Rec = "6.2.2" # Prepare failure reasons and details based on compliance
$auditResult.ELevel = "E3" # Condition B: Prepare failure reasons based on the presence of whitelisted rules
$auditResult.ProfileLevel = "L1" $failureReasons = if ($whitelistedRules) {
$auditResult.IG1 = $false "There are transport rules whitelisting specific domains."
$auditResult.IG2 = $false }
$auditResult.IG3 = $false else {
$auditResult.RecDescription = "Ensure mail transport rules do not whitelist specific domains" "N/A"
}
if ($whitelistedRules) { # Condition C: Prepare details about the whitelisted rules if found
$ruleDetails = $whitelistedRules | ForEach-Object { "{0}: {1}" -f $_.Name, ($_.SenderDomainIs -join ', ') } $details = if ($whitelistedRules) {
$auditResult.Result = $false $ruleDetails = $whitelistedRules | ForEach-Object { "{0}: {1}" -f $_.Name, ($_.SenderDomainIs -join ', ') }
$auditResult.Details = "Whitelisted Rules: $($ruleDetails -join '; ')" "Whitelisted Rules: $($ruleDetails -join '; ')"
$auditResult.FailureReason = "There are transport rules whitelisting specific domains." }
$auditResult.Status = "Fail" else {
} else { "No transport rules whitelisting specific domains found."
$auditResult.Result = $true }
$auditResult.Details = "No transport rules whitelisting specific domains found."
$auditResult.FailureReason = "N/A" # Create and populate the CISAuditResult object
$auditResult.Status = "Pass" $params = @{
Rec = $recnum
Result = -not $whitelistedRules
Status = if ($whitelistedRules) { "Fail" } else { "Pass" }
Details = $details
FailureReason = $failureReasons
}
$auditResult = Initialize-CISAuditResult @params
}
catch {
Write-Error "An error occurred during the test: $_"
# Retrieve the description from the test definitions
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
} }
} }

View File

@@ -1,52 +1,94 @@
function Test-NotifyMalwareInternal { function Test-NotifyMalwareInternal {
[CmdletBinding()] [CmdletBinding()]
[OutputType([CISAuditResult])]
param ( param (
# Aligned
# Parameters can be added if needed # Parameters can be added if needed
) )
begin { begin {
<#
# Conditions for 2.1.3 (L1) Ensure notifications for internal users sending malware is Enabled
#
# Validate test for a pass:
# - Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
# - Specific conditions to check:
# - Condition A: Notifications for internal users sending malware are enabled in the Microsoft 365 Security & Compliance Center.
# - Condition B: Using PowerShell, the `NotifyInternal` property in the anti-malware policy is set to `True` and includes at least one valid email address for notifications.
#
# Validate test for a fail:
# - Confirm that the failure conditions in the automated test are consistent with the manual audit results.
# - Specific conditions to check:
# - Condition A: Notifications for internal users sending malware are not enabled in the Microsoft 365 Security & Compliance Center.
# - Condition B: Using PowerShell, the `NotifyInternal` property in the anti-malware policy is set to `False` or does not include any valid email addresses for notifications.
#>
$auditResults = @() # Dot source the class script if necessary
#. .\source\Classes\CISAuditResult.ps1
# Initialization code, if needed
$recnum = "2.1.3"
} }
process { process {
# Retrieve all 'Custom' malware filter policies and check notification settings try {
$malwareNotifications = Get-MalwareFilterPolicy | Where-Object { $_.RecommendedPolicyType -eq 'Custom' } # 2.1.3 Ensure notifications for internal users sending malware is Enabled
$policiesToReport = @()
foreach ($policy in $malwareNotifications) { # Retrieve all 'Custom' malware filter policies and check notification settings
if ($policy.EnableInternalSenderAdminNotifications -ne $true) { $malwareNotifications = Get-MalwareFilterPolicy | Where-Object { $_.RecommendedPolicyType -eq 'Custom' }
$policiesToReport += "$($policy.Identity): Notifications Disabled"
# Condition B: Using PowerShell, the `NotifyInternal` property in the anti-malware policy is set to `True` and includes at least one valid email address for notifications.
$policiesToReport = @()
foreach ($policy in $malwareNotifications) {
if ($policy.EnableInternalSenderAdminNotifications -ne $true) {
$policiesToReport += "$($policy.Identity): Notifications Disabled"
}
} }
# Determine the result based on the presence of custom policies without notifications
$result = $policiesToReport.Count -eq 0
# Prepare failure reasons and details based on compliance
$failureReasons = if ($result) {
"N/A"
}
else {
# Condition A: Notifications for internal users sending malware are not enabled in the Microsoft 365 Security & Compliance Center.
"Some custom policies do not have notifications for internal users sending malware enabled."
}
$details = if ($result) {
"All custom malware policies have notifications enabled."
}
else {
"Misconfigured Policies: $($policiesToReport -join ', ')"
}
# Create and populate the CISAuditResult object
$params = @{
Rec = $recnum
Result = $result
Status = if ($result) { "Pass" } else { "Fail" }
Details = $details
FailureReason = $failureReasons
}
$auditResult = Initialize-CISAuditResult @params
} }
catch {
Write-Error "An error occurred during the test: $_"
# Determine the result based on the presence of custom policies without notifications # Retrieve the description from the test definitions
$result = $policiesToReport.Count -eq 0 $testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$details = if ($result) { "All custom malware policies have notifications enabled." } else { "Misconfigured Policies: $($policiesToReport -join ', ')" } $description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$failureReason = if ($result) { "N/A" } else { "Some custom policies do not have notifications for internal users sending malware enabled." }
# Create an instance of CISAuditResult and populate it $script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
$auditResult = [CISAuditResult]::new()
$auditResult.Status = if ($result) { "Pass" } else { "Fail" }
$auditResult.ELevel = "E3"
$auditResult.ProfileLevel = "L1"
$auditResult.Rec = "2.1.3"
$auditResult.RecDescription = "Ensure notifications for internal users sending malware is Enabled"
$auditResult.CISControlVer = "v8"
$auditResult.CISControl = "17.5"
$auditResult.CISDescription = "Assign Key Roles and Responsibilities"
$auditResult.IG1 = $false
$auditResult.IG2 = $true
$auditResult.IG3 = $true
$auditResult.Result = $result
$auditResult.Details = $details
$auditResult.FailureReason = $failureReason
$auditResults += $auditResult # Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
} }
end { end {
# Return auditResults # Return the audit result
return $auditResults return $auditResult
} }
} }

View File

@@ -1,37 +1,79 @@
function Test-OneDriveContentRestrictions { function Test-OneDriveContentRestrictions {
[CmdletBinding()] [CmdletBinding()]
[OutputType([CISAuditResult])]
param ( param (
# Aligned
# Define your parameters here # Define your parameters here
) )
begin { begin {
# Initialization code # 7.2.4 (L2) Ensure OneDrive content sharing is restricted
#
# Validate test for a pass:
# - Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
# - Specific conditions to check:
# - Condition A: The OneDriveSharingCapability setting is configured to "Disabled" using the PowerShell cmdlet `Get-SPOTenant | fl OneDriveSharingCapability`.
# - Condition B: The OneDriveSharingCapability is set to "Only people in your organization" in the SharePoint admin center under Policies > Sharing > OneDrive.
# - Condition C: OneDrive content sharing is not more permissive than SharePoint content sharing.
#
# Validate test for a fail:
# - Confirm that the failure conditions in the automated test are consistent with the manual audit results.
# - Specific conditions to check:
# - Condition A: The OneDriveSharingCapability setting is not configured to "Disabled" using the PowerShell cmdlet `Get-SPOTenant | fl OneDriveSharingCapability`.
# - Condition B: The OneDriveSharingCapability is not set to "Only people in your organization" in the SharePoint admin center under Policies > Sharing > OneDrive.
# - Condition C: OneDrive content sharing is more permissive than SharePoint content sharing.
$auditResult = [CISAuditResult]::new() # Dot source the class script if necessary
#. .\source\Classes\CISAuditResult.ps1
# Initialization code, if needed
$recnum = "7.2.4"
} }
process { process {
# 7.2.4 (L2) Ensure OneDrive content sharing is restricted try {
$SPOTenant = Get-SPOTenant | Select-Object OneDriveSharingCapability # 7.2.4 (L2) Ensure OneDrive content sharing is restricted
$isOneDriveSharingRestricted = $SPOTenant.OneDriveSharingCapability -eq 'Disabled'
# Populate the auditResult object with the required properties # Retrieve OneDrive sharing capability settings
$auditResult.CISControlVer = "v8" $SPOTenant = Get-SPOTenant | Select-Object OneDriveSharingCapability
$auditResult.CISControl = "3.3" $isOneDriveSharingRestricted = $SPOTenant.OneDriveSharingCapability -eq 'Disabled'
$auditResult.CISDescription = "Configure Data Access Control Lists"
$auditResult.Rec = "7.2.4" # Prepare failure reasons and details based on compliance
$auditResult.ELevel = "E3" $failureReasons = if (-not $isOneDriveSharingRestricted) {
$auditResult.ProfileLevel = "L2" "OneDrive content sharing is not restricted to 'Disabled'. Current setting: $($SPOTenant.OneDriveSharingCapability)"
$auditResult.IG1 = $true }
$auditResult.IG2 = $true else {
$auditResult.IG3 = $true "N/A"
$auditResult.RecDescription = "Ensure OneDrive content sharing is restricted" }
$auditResult.Result = $isOneDriveSharingRestricted $details = if ($isOneDriveSharingRestricted) {
$auditResult.Details = "OneDriveSharingCapability: $($SPOTenant.OneDriveSharingCapability)" "OneDrive content sharing is restricted."
$auditResult.FailureReason = if (-not $isOneDriveSharingRestricted) { "OneDrive content sharing is not restricted to 'Disabled'. Current setting: $($SPOTenant.OneDriveSharingCapability)" } else { "N/A" } }
$auditResult.Status = if ($isOneDriveSharingRestricted) { "Pass" } else { "Fail" } else {
"OneDriveSharingCapability: $($SPOTenant.OneDriveSharingCapability)"
}
# Create and populate the CISAuditResult object
$params = @{
Rec = $recnum
Result = $isOneDriveSharingRestricted
Status = if ($isOneDriveSharingRestricted) { "Pass" } else { "Fail" }
Details = $details
FailureReason = $failureReasons
}
$auditResult = Initialize-CISAuditResult @params
}
catch {
Write-Error "An error occurred during the test: $_"
# Retrieve the description from the test definitions
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
} }
end { end {

View File

@@ -1,37 +1,79 @@
function Test-OneDriveSyncRestrictions { function Test-OneDriveSyncRestrictions {
[CmdletBinding()] [CmdletBinding()]
[OutputType([CISAuditResult])]
param ( param (
# Aligned
# Define your parameters here # Define your parameters here
) )
begin { begin {
# Initialization code # Dot source the class script if necessary
#. .\source\Classes\CISAuditResult.ps1
$auditResult = [CISAuditResult]::new() # Initialization code, if needed
$recnum = "7.3.2"
} }
process { process {
# 7.3.2 (L2) Ensure OneDrive sync is restricted for unmanaged devices try {
$SPOTenantSyncClientRestriction = Get-SPOTenantSyncClientRestriction | Select-Object TenantRestrictionEnabled, AllowedDomainList # 7.3.2 (L2) Ensure OneDrive sync is restricted for unmanaged devices
$isSyncRestricted = $SPOTenantSyncClientRestriction.TenantRestrictionEnabled -and $SPOTenantSyncClientRestriction.AllowedDomainList #
# Validate test for a pass:
# - Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
# - Specific conditions to check:
# - Condition A: Verify that "Allow syncing only on computers joined to specific domains" is enabled.
# - Condition B: Check that "TenantRestrictionEnabled" is set to True.
# - Condition C: Ensure that "AllowedDomainList" contains the trusted domain GUIDs from the on-premises environment.
#
# Validate test for a fail:
# - Confirm that the failure conditions in the automated test are consistent with the manual audit results.
# - Specific conditions to check:
# - Condition A: "Allow syncing only on computers joined to specific domains" is not enabled.
# - Condition B: "TenantRestrictionEnabled" is set to False.
# - Condition C: "AllowedDomainList" does not contain the trusted domain GUIDs from the on-premises environment.
# Populate the auditResult object with the required properties # Retrieve OneDrive sync client restriction settings
$auditResult.CISControlVer = "v8" $SPOTenantSyncClientRestriction = Get-SPOTenantSyncClientRestriction | Select-Object TenantRestrictionEnabled, AllowedDomainList
$auditResult.CISControl = "0.0" $isSyncRestricted = $SPOTenantSyncClientRestriction.TenantRestrictionEnabled -and $SPOTenantSyncClientRestriction.AllowedDomainList
$auditResult.CISDescription = "Explicitly Not Mapped"
$auditResult.Rec = "7.3.2" # Condition A: Check if TenantRestrictionEnabled is True
$auditResult.ELevel = "E3" # Condition B: Ensure AllowedDomainList contains trusted domains GUIDs
$auditResult.ProfileLevel = "L2" $failureReasons = if (-not $isSyncRestricted) {
$auditResult.IG1 = $false "OneDrive sync is not restricted to managed devices. TenantRestrictionEnabled should be True and AllowedDomainList should contain trusted domains GUIDs."
$auditResult.IG2 = $false }
$auditResult.IG3 = $false else {
$auditResult.RecDescription = "Ensure OneDrive sync is restricted for unmanaged devices" "N/A"
}
$auditResult.Result = $isSyncRestricted # Condition C: Prepare details based on whether sync is restricted
$auditResult.Details = "TenantRestrictionEnabled: $($SPOTenantSyncClientRestriction.TenantRestrictionEnabled); AllowedDomainList: $($SPOTenantSyncClientRestriction.AllowedDomainList -join ', ')" $details = if ($isSyncRestricted) {
$auditResult.FailureReason = if (-not $isSyncRestricted) { "OneDrive sync is not restricted to managed devices. TenantRestrictionEnabled should be True and AllowedDomainList should contain trusted domains GUIDs." } else { "N/A" } "OneDrive sync is restricted for unmanaged devices."
$auditResult.Status = if ($isSyncRestricted) { "Pass" } else { "Fail" } }
else {
"TenantRestrictionEnabled: $($SPOTenantSyncClientRestriction.TenantRestrictionEnabled); AllowedDomainList: $($SPOTenantSyncClientRestriction.AllowedDomainList -join ', ')"
}
# Create and populate the CISAuditResult object
$params = @{
Rec = $recnum
Result = $isSyncRestricted
Status = if ($isSyncRestricted) { "Pass" } else { "Fail" }
Details = $details
FailureReason = $failureReasons
}
$auditResult = Initialize-CISAuditResult @params
}
catch {
Write-Error "An error occurred during the test: $_"
# Retrieve the description from the test definitions
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
} }
end { end {

View File

@@ -1,45 +1,86 @@
function Test-OrgOnlyBypassLobby { function Test-OrgOnlyBypassLobby {
[CmdletBinding()] [CmdletBinding()]
[OutputType([CISAuditResult])]
param ( param (
# Aligned
# Parameters can be defined here if needed # Parameters can be defined here if needed
) )
begin { begin {
# Dot source the class script # Dot source the class script if necessary
#. .\source\Classes\CISAuditResult.ps1
$auditResults = @() # Initialization code, if needed
$recnum = "8.5.3"
} }
process { process {
# 8.5.3 (L1) Ensure only people in my org can bypass the lobby try {
# 8.5.3 (L1) Ensure only people in my org can bypass the lobby
#
# Validate test for a pass:
# - Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
# - Specific conditions to check:
# - Condition A: The `AutoAdmittedUsers` setting in the Teams meeting policy is set to `EveryoneInCompanyExcludingGuests`.
# - Condition B: The setting for "Who can bypass the lobby" is configured to "People in my org" using the UI.
# - Condition C: Verification using the Microsoft Teams admin center confirms that the meeting join & lobby settings are configured as recommended.
#
# Validate test for a fail:
# - Confirm that the failure conditions in the automated test are consistent with the manual audit results.
# - Specific conditions to check:
# - Condition A: The `AutoAdmittedUsers` setting in the Teams meeting policy is not set to `EveryoneInCompanyExcludingGuests`.
# - Condition B: The setting for "Who can bypass the lobby" is not configured to "People in my org" using the UI.
# - Condition C: Verification using the Microsoft Teams admin center indicates that the meeting join & lobby settings are not configured as recommended.
# Connect to Teams PowerShell using Connect-MicrosoftTeams # Connect to Teams PowerShell using Connect-MicrosoftTeams
$CsTeamsMeetingPolicyLobby = Get-CsTeamsMeetingPolicy -Identity Global | Select-Object -Property AutoAdmittedUsers # Retrieve the Teams meeting policy for lobby bypass settings
$lobbyBypassRestricted = $CsTeamsMeetingPolicyLobby.AutoAdmittedUsers -eq 'EveryoneInCompanyExcludingGuests' $CsTeamsMeetingPolicyLobby = Get-CsTeamsMeetingPolicy -Identity Global | Select-Object -Property AutoAdmittedUsers
$lobbyBypassRestricted = $CsTeamsMeetingPolicyLobby.AutoAdmittedUsers -eq 'EveryoneInCompanyExcludingGuests'
# Create an instance of CISAuditResult and populate it # Prepare failure reasons and details based on compliance
$auditResult = [CISAuditResult]::new() $failureReasons = if (-not $lobbyBypassRestricted) {
$auditResult.CISControlVer = "v8" # Condition A: The `AutoAdmittedUsers` setting in the Teams meeting policy is not set to `EveryoneInCompanyExcludingGuests`.
$auditResult.CISControl = "6.8" "External participants can bypass the lobby"
$auditResult.CISDescription = "Define and Maintain Role-Based Access Control" }
$auditResult.Rec = "8.5.3" else {
$auditResult.ELevel = "E3" "N/A"
$auditResult.ProfileLevel = "L1" }
$auditResult.IG1 = $false # Set based on the CIS Controls image
$auditResult.IG2 = $false # Set based on the CIS Controls image
$auditResult.IG3 = $true # Set based on the CIS Controls image
$auditResult.RecDescription = "Ensure only people in my org can bypass the lobby"
$auditResult.Result = $lobbyBypassRestricted
$auditResult.Details = "AutoAdmittedUsers is set to $($CsTeamsMeetingPolicyLobby.AutoAdmittedUsers)"
$auditResult.FailureReason = if ($lobbyBypassRestricted) { "N/A" } else { "External participants can bypass the lobby" }
$auditResult.Status = if ($lobbyBypassRestricted) { "Pass" } else { "Fail" }
$auditResults += $auditResult $details = if ($lobbyBypassRestricted) {
# Condition B: The setting for "Who can bypass the lobby" is configured to "People in my org" using the UI.
"Only people in the organization can bypass the lobby."
}
else {
# Condition C: Verification using the Microsoft Teams admin center indicates that the meeting join & lobby settings are not configured as recommended.
"AutoAdmittedUsers is set to $($CsTeamsMeetingPolicyLobby.AutoAdmittedUsers)"
}
# Create and populate the CISAuditResult object
$params = @{
Rec = $recnum
Result = $lobbyBypassRestricted
Status = if ($lobbyBypassRestricted) { "Pass" } else { "Fail" }
Details = $details
FailureReason = $failureReasons
}
$auditResult = Initialize-CISAuditResult @params
}
catch {
Write-Error "An error occurred during the test: $_"
# Retrieve the description from the test definitions
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
} }
end { end {
# Return auditResults # Return the audit result
return $auditResults return $auditResult
} }
} }

View File

@@ -1,45 +1,81 @@
function Test-OrganizersPresent { function Test-OrganizersPresent {
[CmdletBinding()] [CmdletBinding()]
[OutputType([CISAuditResult])]
param ( param (
# Aligned
# Parameters can be defined here if needed # Parameters can be defined here if needed
) )
begin { begin {
# Dot source the class script # Dot source the class script if necessary
#. .\source\Classes\CISAuditResult.ps1
$auditResults = @() # Initialization code, if needed
$recnum = "8.5.6"
} }
process { process {
# 8.5.6 (L2) Ensure only organizers and co-organizers can present try {
# 8.5.6 (L2) Ensure only organizers and co-organizers can present
#
# Validate test for a pass:
# - Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
# - Specific conditions to check:
# - Condition A: The `DesignatedPresenterRoleMode` setting in the Teams meeting policy is set to `OrganizerOnlyUserOverride`.
# - Condition B: Verification using the Teams admin center confirms that the setting "Who can present" is configured to "Only organizers and co-organizers".
# - Condition C: Verification using PowerShell confirms that the `DesignatedPresenterRoleMode` is set to `OrganizerOnlyUserOverride`.
#
# Validate test for a fail:
# - Confirm that the failure conditions in the automated test are consistent with the manual audit results.
# - Specific conditions to check:
# - Condition A: The `DesignatedPresenterRoleMode` setting in the Teams meeting policy is not set to `OrganizerOnlyUserOverride`.
# - Condition B: Verification using the Teams admin center indicates that the setting "Who can present" is not configured to "Only organizers and co-organizers".
# - Condition C: Verification using PowerShell indicates that the `DesignatedPresenterRoleMode` is not set to `OrganizerOnlyUserOverride`.
# Connect to Teams PowerShell using Connect-MicrosoftTeams # Retrieve the Teams meeting policy for presenters
$CsTeamsMeetingPolicyPresenters = Get-CsTeamsMeetingPolicy -Identity Global | Select-Object -Property DesignatedPresenterRoleMode
$presenterRoleRestricted = $CsTeamsMeetingPolicyPresenters.DesignatedPresenterRoleMode -eq 'OrganizerOnlyUserOverride'
$CsTeamsMeetingPolicyPresenters = Get-CsTeamsMeetingPolicy -Identity Global | Select-Object -Property DesignatedPresenterRoleMode # Prepare failure reasons and details based on compliance
$presenterRoleRestricted = $CsTeamsMeetingPolicyPresenters.DesignatedPresenterRoleMode -eq 'OrganizerOnlyUserOverride' $failureReasons = if (-not $presenterRoleRestricted) {
"Others besides organizers and co-organizers can present"
}
else {
"N/A"
}
# Create an instance of CISAuditResult and populate it $details = if ($presenterRoleRestricted) {
$auditResult = [CISAuditResult]::new() "Only organizers and co-organizers can present."
$auditResult.CISControlVer = "v8" }
$auditResult.CISControl = "0.0" # Explicitly Not Mapped as per the image provided else {
$auditResult.CISDescription = "Explicitly Not Mapped" "DesignatedPresenterRoleMode is set to $($CsTeamsMeetingPolicyPresenters.DesignatedPresenterRoleMode)"
$auditResult.Rec = "8.5.6" }
$auditResult.ELevel = "E3"
$auditResult.ProfileLevel = "L1"
$auditResult.IG1 = $false # Set based on the CIS Controls image
$auditResult.IG2 = $false # Set based on the CIS Controls image
$auditResult.IG3 = $false # Set based on the CIS Controls image
$auditResult.RecDescription = "Ensure only organizers and co-organizers can present"
$auditResult.Result = $presenterRoleRestricted
$auditResult.Details = "DesignatedPresenterRoleMode is set to $($CsTeamsMeetingPolicyPresenters.DesignatedPresenterRoleMode)"
$auditResult.FailureReason = if ($presenterRoleRestricted) { "N/A" } else { "Others besides organizers and co-organizers can present" }
$auditResult.Status = if ($presenterRoleRestricted) { "Pass" } else { "Fail" }
$auditResults += $auditResult # Create and populate the CISAuditResult object
$params = @{
Rec = $recnum
Result = $presenterRoleRestricted
Status = if ($presenterRoleRestricted) { "Pass" } else { "Fail" }
Details = $details
FailureReason = $failureReasons
}
$auditResult = Initialize-CISAuditResult @params
}
catch {
Write-Error "An error occurred during the test: $_"
# Retrieve the description from the test definitions
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
} }
end { end {
# Return auditResults # Return the audit result
return $auditResults return $auditResult
} }
} }

View File

@@ -1,43 +1,78 @@
function Test-PasswordHashSync { function Test-PasswordHashSync {
[CmdletBinding()] [CmdletBinding()]
[OutputType([CISAuditResult])]
param ( param (
# Aligned
# Parameters can be added if needed # Parameters can be added if needed
) )
begin { begin {
# Dot source the class script # Conditions for 5.1.8.1 (L1) Ensure password hash sync is enabled for hybrid deployments
#
# Validate test for a pass:
# - Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
# - Specific conditions to check:
# - Condition A: Password hash sync is enabled in the Azure AD Connect tool on the on-premises server.
# - Condition B: Password hash sync is verified as enabled in the Azure AD Connect Sync section in the Microsoft Entra admin center.
# - Condition C: Using Microsoft Graph PowerShell, the verification command returns the expected result indicating that password sync is enabled for the on-premises AD.
#
# Validate test for a fail:
# - Confirm that the failure conditions in the automated test are consistent with the manual audit results.
# - Specific conditions to check:
# - Condition A: Password hash sync is not enabled in the Azure AD Connect tool on the on-premises server.
# - Condition B: Password hash sync is not verified as enabled in the Azure AD Connect Sync section in the Microsoft Entra admin center.
# - Condition C: Using Microsoft Graph PowerShell, the verification command returns no result indicating that password sync is not enabled for the on-premises AD.
$auditResults = @() # Dot source the class script if necessary
#. .\source\Classes\CISAuditResult.ps1
# Initialization code, if needed
$recnum = "5.1.8.1"
} }
process { process {
# 5.1.8.1 (L1) Ensure password hash sync is enabled for hybrid deployments try {
# Pass if OnPremisesSyncEnabled is True. Fail otherwise. # 5.1.8.1 (L1) Ensure password hash sync is enabled for hybrid deployments
$passwordHashSync = Get-MgOrganization | Select-Object OnPremisesSyncEnabled
$hashSyncResult = $passwordHashSync.OnPremisesSyncEnabled
# Create an instance of CISAuditResult and populate it # Retrieve password hash sync status (Condition A and C)
$auditResult = [CISAuditResult]::new() $passwordHashSync = Get-MgOrganization | Select-Object -ExpandProperty OnPremisesSyncEnabled
$auditResult.Status = if ($hashSyncResult) { "Pass" } else { "Fail" } $hashSyncResult = $passwordHashSync
$auditResult.ELevel = "E3"
$auditResult.ProfileLevel = "L1"
$auditResult.Rec = "5.1.8.1"
$auditResult.RecDescription = "Ensure password hash sync is enabled for hybrid deployments"
$auditResult.CISControlVer = "v8"
$auditResult.CISControl = "6.7"
$auditResult.CISDescription = "Centralize Access Control"
$auditResult.IG1 = $false
$auditResult.IG2 = $true
$auditResult.IG3 = $true
$auditResult.Result = $hashSyncResult
$auditResult.Details = "OnPremisesSyncEnabled: $($passwordHashSync.OnPremisesSyncEnabled)"
$auditResult.FailureReason = if (-not $hashSyncResult) { "Password hash sync for hybrid deployments is not enabled" } else { "N/A" }
$auditResults += $auditResult # Prepare failure reasons and details based on compliance
$failureReasons = if (-not $hashSyncResult) {
"Password hash sync for hybrid deployments is not enabled"
}
else {
"N/A"
}
$details = "OnPremisesSyncEnabled: $($passwordHashSync)"
# Create and populate the CISAuditResult object
$params = @{
Rec = $recnum
Result = $hashSyncResult
Status = if ($hashSyncResult) { "Pass" } else { "Fail" }
Details = $details
FailureReason = $failureReasons
}
$auditResult = Initialize-CISAuditResult @params
}
catch {
Write-Error "An error occurred during the test: $_"
# Retrieve the description from the test definitions
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
} }
end { end {
# Return auditResults # Return the audit result
return $auditResults return $auditResult
} }
} }

View File

@@ -1,45 +1,102 @@
function Test-PasswordNeverExpirePolicy { function Test-PasswordNeverExpirePolicy {
[CmdletBinding()] [CmdletBinding()]
[OutputType([CISAuditResult])]
param ( param (
[Parameter(Mandatory)] [Parameter(Mandatory = $false)]
[string]$DomainName # DomainName parameter is now mandatory [string]$DomainName
) )
begin { begin {
# Dot source the class script # .TODO add supported services to output details. ({Email, OfficeCommunicationsOnline, Intune})
# Dot source the class script if necessary
#. .\source\Classes\CISAuditResult.ps1
# Initialization code, if needed
$recnum = "1.3.1"
$overallResult = $true
$detailsList = @()
$failureReasonsList = @()
$auditResults = @() # Add headers for the details
$detailsList += "Domain|Validity Period|IsDefault"
# Conditions for 1.3.1 (L1) Ensure the 'Password expiration policy' is set to 'Set passwords to never expire (recommended)'
#
# Validate test for a pass:
# - Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
# - Specific conditions to check:
# - Condition A: Password expiration policy is set to "Set passwords to never expire" in the Microsoft 365 admin center.
# - Condition B: Using Microsoft Graph PowerShell, the `PasswordPolicies` property for all users is set to `DisablePasswordExpiration`.
#
# Validate test for a fail:
# - Confirm that the failure conditions in the automated test are consistent with the manual audit results.
# - Specific conditions to check:
# - Condition A: Password expiration policy is not set to "Set passwords to never expire" in the Microsoft 365 admin center.
# - Condition B: Using Microsoft Graph PowerShell, the `PasswordPolicies` property for one or more users is not set to `DisablePasswordExpiration`.
} }
process { process {
# 1.3.1 (L1) Ensure the 'Password expiration policy' is set to 'Set passwords to never expire' try {
# Pass if PasswordValidityPeriodInDays is 0. # Step: Retrieve all domains or a specific domain
# Fail otherwise. $domains = if ($DomainName) {
Get-MgDomain -DomainId $DomainName
} else {
Get-MgDomain
}
$passwordPolicy = Get-MgDomain -DomainId $DomainName | Select-Object PasswordValidityPeriodInDays foreach ($domain in $domains) {
$domainName = $domain.Id
$isDefault = $domain.IsDefault
# Step (Condition A): Retrieve password expiration policy
$passwordPolicy = $domain.PasswordValidityPeriodInDays
# Create an instance of CISAuditResult and populate it # Step (Condition A & B): Determine if the policy is compliant
$auditResult = [CISAuditResult]::new() $isCompliant = $passwordPolicy -eq 0
$auditResult.Rec = "1.3.1" $overallResult = $overallResult -and $isCompliant
$auditResult.RecDescription = "Ensure the 'Password expiration policy' is set to 'Set passwords to never expire'"
$auditResult.ELevel = "E3"
$auditResult.ProfileLevel = "L1"
$auditResult.CISControlVer = "v8"
$auditResult.CISControl = "5.2"
$auditResult.CISDescription = "Use Unique Passwords"
$auditResult.IG1 = $true
$auditResult.IG2 = $true
$auditResult.IG3 = $true # All are true
$auditResult.Result = $passwordPolicy.PasswordValidityPeriodInDays -eq 0
$auditResult.Details = "Validity Period: $($passwordPolicy.PasswordValidityPeriodInDays) days"
$auditResult.FailureReason = if ($passwordPolicy.PasswordValidityPeriodInDays -eq 0) { "N/A" } else { "Password expiration is not set to never expire" }
$auditResult.Status = if ($passwordPolicy.PasswordValidityPeriodInDays -eq 0) { "Pass" } else { "Fail" }
$auditResults += $auditResult # Step (Condition A & B): Prepare failure reasons and details based on compliance
$failureReasons = if ($isCompliant) {
"N/A"
} else {
"Password expiration is not set to never expire for domain $domainName. Run the following command to remediate: `nUpdate-MgDomain -DomainId $domainName -PasswordValidityPeriodInDays 2147483647 -PasswordNotificationWindowInDays 30`n"
}
$details = "$domainName|$passwordPolicy days|$isDefault"
# Add details and failure reasons to the lists
$detailsList += $details
$failureReasonsList += $failureReasons
}
# Prepare the final failure reason and details
$finalFailureReason = $failureReasonsList -join "`n"
$finalDetails = $detailsList -join "`n"
# Step: Create and populate the CISAuditResult object
$params = @{
Rec = $recnum
Result = $overallResult
Status = if ($overallResult) { "Pass" } else { "Fail" }
Details = $finalDetails
FailureReason = $finalFailureReason
}
$auditResult = Initialize-CISAuditResult @params
}
catch {
Write-Error "An error occurred during the test: $_"
# Retrieve the description from the test definitions
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
} }
end { end {
# Return auditResults # Return the audit result
return $auditResults return $auditResult
} }
} }

View File

@@ -1,37 +1,74 @@
function Test-ReauthWithCode { function Test-ReauthWithCode {
[CmdletBinding()] [CmdletBinding()]
[OutputType([CISAuditResult])]
param ( param (
# Aligned
# Define your parameters here # Define your parameters here
) )
begin { begin {
# Initialization code <#
Conditions for 7.2.10 (L1) Ensure reauthentication with verification code is restricted
#
# Validate test for a pass:
# - Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
# - Specific conditions to check:
# - Condition A: The `EmailAttestationRequired` property is set to `True`.
# - Condition B: The `EmailAttestationReAuthDays` property is set to `15` or less.
#
# Validate test for a fail:
# - Confirm that the failure conditions in the automated test are consistent with the manual audit results.
# - Specific conditions to check:
# - Condition A: The `EmailAttestationRequired` property is set to `False`.
# - Condition B: The `EmailAttestationReAuthDays` property is set to more than `15`.
#>
$auditResult = [CISAuditResult]::new() # Dot source the class script if necessary
#. .\source\Classes\CISAuditResult.ps1
# Initialization code, if needed
$recnum = "7.2.10"
} }
process { process {
# 7.2.10 (L1) Ensure reauthentication with verification code is restricted try {
$SPOTenantReauthentication = Get-SPOTenant | Select-Object EmailAttestationRequired, EmailAttestationReAuthDays # 7.2.10 (L1) Ensure reauthentication with verification code is restricted
$isReauthenticationRestricted = $SPOTenantReauthentication.EmailAttestationRequired -and $SPOTenantReauthentication.EmailAttestationReAuthDays -le 15
# Populate the auditResult object with the required properties # Retrieve reauthentication settings for SharePoint Online
$auditResult.CISControlVer = "v8" $SPOTenantReauthentication = Get-SPOTenant | Select-Object EmailAttestationRequired, EmailAttestationReAuthDays
$auditResult.CISControl = "0.0" $isReauthenticationRestricted = $SPOTenantReauthentication.EmailAttestationRequired -and $SPOTenantReauthentication.EmailAttestationReAuthDays -le 15
$auditResult.CISDescription = "Explicitly Not Mapped"
$auditResult.Rec = "7.2.10" # Prepare failure reasons and details based on compliance
$auditResult.ELevel = "E3" $failureReasons = if (-not $isReauthenticationRestricted) {
$auditResult.ProfileLevel = "L1" "Reauthentication with verification code does not require reauthentication within 15 days or less."
$auditResult.IG1 = $false }
$auditResult.IG2 = $false else {
$auditResult.IG3 = $false "N/A"
$auditResult.RecDescription = "Ensure reauthentication with verification code is restricted" }
$auditResult.Result = $isReauthenticationRestricted $details = "EmailAttestationRequired: $($SPOTenantReauthentication.EmailAttestationRequired); EmailAttestationReAuthDays: $($SPOTenantReauthentication.EmailAttestationReAuthDays)"
$auditResult.Details = "EmailAttestationRequired: $($SPOTenantReauthentication.EmailAttestationRequired); EmailAttestationReAuthDays: $($SPOTenantReauthentication.EmailAttestationReAuthDays)"
$auditResult.FailureReason = if (-not $isReauthenticationRestricted) { "Reauthentication with verification code does not require reauthentication within 15 days or less." } else { "N/A" } # Create and populate the CISAuditResult object
$auditResult.Status = if ($isReauthenticationRestricted) { "Pass" } else { "Fail" } $params = @{
Rec = $recnum
Result = $isReauthenticationRestricted
Status = if ($isReauthenticationRestricted) { "Pass" } else { "Fail" }
Details = $details
FailureReason = $failureReasons
}
$auditResult = Initialize-CISAuditResult @params
}
catch {
Write-Error "An error occurred during the test: $_"
# Retrieve the description from the test definitions
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
} }
end { end {

View File

@@ -1,56 +1,78 @@
function Test-ReportSecurityInTeams { function Test-ReportSecurityInTeams {
[CmdletBinding()] [CmdletBinding()]
[OutputType([CISAuditResult])]
param ( param (
# Aligned
# Parameters can be defined here if needed # Parameters can be defined here if needed
) )
begin { begin {
# Dot source the class script # Dot source the class script if necessary
#. .\source\Classes\CISAuditResult.ps1
$auditResults = @() # Initialization code, if needed
$recnum = "8.6.1"
} }
process { process {
# 8.6.1 (L1) Ensure users can report security concerns in Teams try {
# 8.6.1 (L1) Ensure users can report security concerns in Teams
# Connect to Teams PowerShell using Connect-MicrosoftTeams # Retrieve the necessary settings for Teams and Exchange Online
# Connect to Exchange Online PowerShell using Connect-ExchangeOnline # Condition A: Ensure the 'Report a security concern' setting in the Teams admin center is set to 'On'.
$CsTeamsMessagingPolicy = Get-CsTeamsMessagingPolicy -Identity Global | Select-Object -Property AllowSecurityEndUserReporting
$CsTeamsMessagingPolicy = Get-CsTeamsMessagingPolicy -Identity Global | Select-Object -Property AllowSecurityEndUserReporting # Condition B: Verify that 'Monitor reported messages in Microsoft Teams' is checked in the Microsoft 365 Defender portal.
$ReportSubmissionPolicy = Get-ReportSubmissionPolicy | Select-Object -Property ReportJunkToCustomizedAddress, ReportNotJunkToCustomizedAddress, ReportPhishToCustomizedAddress, ReportChatMessageToCustomizedAddressEnabled # Condition C: Ensure the 'Send reported messages to' setting in the Microsoft 365 Defender portal is set to 'My reporting mailbox only' with the correct report email addresses.
$ReportSubmissionPolicy = Get-ReportSubmissionPolicy | Select-Object -Property ReportJunkToCustomizedAddress, ReportNotJunkToCustomizedAddress, ReportPhishToCustomizedAddress, ReportChatMessageToCustomizedAddressEnabled
$securityReportEnabled = $CsTeamsMessagingPolicy.AllowSecurityEndUserReporting -and # Check if all the required settings are enabled
$ReportSubmissionPolicy.ReportJunkToCustomizedAddress -and $securityReportEnabled = $CsTeamsMessagingPolicy.AllowSecurityEndUserReporting -and
$ReportSubmissionPolicy.ReportNotJunkToCustomizedAddress -and $ReportSubmissionPolicy.ReportJunkToCustomizedAddress -and
$ReportSubmissionPolicy.ReportPhishToCustomizedAddress -and $ReportSubmissionPolicy.ReportNotJunkToCustomizedAddress -and
$ReportSubmissionPolicy.ReportChatMessageToCustomizedAddressEnabled $ReportSubmissionPolicy.ReportPhishToCustomizedAddress -and
$ReportSubmissionPolicy.ReportChatMessageToCustomizedAddressEnabled
# Create an instance of CISAuditResult and populate it # Prepare failure reasons and details based on compliance
$auditResult = [CISAuditResult]::new() $failureReasons = if (-not $securityReportEnabled) {
$auditResult.CISControlVer = "v8" "Users cannot report security concerns in Teams due to one or more incorrect settings"
$auditResult.CISControl = "0.0" # Explicitly Not Mapped as per the image provided }
$auditResult.CISDescription = "Explicitly Not Mapped" else {
$auditResult.Rec = "8.6.1" "N/A"
$auditResult.ELevel = "E3" }
$auditResult.ProfileLevel = "L1"
$auditResult.IG1 = $false # Set based on the CIS Controls image
$auditResult.IG2 = $false # Set based on the CIS Controls image
$auditResult.IG3 = $false # Set based on the CIS Controls image
$auditResult.RecDescription = "Ensure users can report security concerns in Teams"
$auditResult.Result = $securityReportEnabled
$auditResult.Details = "AllowSecurityEndUserReporting: $($CsTeamsMessagingPolicy.AllowSecurityEndUserReporting); " +
"ReportJunkToCustomizedAddress: $($ReportSubmissionPolicy.ReportJunkToCustomizedAddress); " +
"ReportNotJunkToCustomizedAddress: $($ReportSubmissionPolicy.ReportNotJunkToCustomizedAddress); " +
"ReportPhishToCustomizedAddress: $($ReportSubmissionPolicy.ReportPhishToCustomizedAddress); " +
"ReportChatMessageToCustomizedAddressEnabled: $($ReportSubmissionPolicy.ReportChatMessageToCustomizedAddressEnabled)"
$auditResult.FailureReason = if (-not $securityReportEnabled) { "Users cannot report security concerns in Teams due to one or more incorrect settings" } else { "N/A" }
$auditResult.Status = if ($securityReportEnabled) { "Pass" } else { "Fail" }
$auditResults += $auditResult $details = "AllowSecurityEndUserReporting: $($CsTeamsMessagingPolicy.AllowSecurityEndUserReporting); " +
"ReportJunkToCustomizedAddress: $($ReportSubmissionPolicy.ReportJunkToCustomizedAddress); " +
"ReportNotJunkToCustomizedAddress: $($ReportSubmissionPolicy.ReportNotJunkToCustomizedAddress); " +
"ReportPhishToCustomizedAddress: $($ReportSubmissionPolicy.ReportPhishToCustomizedAddress); " +
"ReportChatMessageToCustomizedAddressEnabled: $($ReportSubmissionPolicy.ReportChatMessageToCustomizedAddressEnabled)"
# Create and populate the CISAuditResult object
$params = @{
Rec = $recnum
Result = $securityReportEnabled
Status = if ($securityReportEnabled) { "Pass" } else { "Fail" }
Details = $details
FailureReason = $failureReasons
}
$auditResult = Initialize-CISAuditResult @params
}
catch {
Write-Error "An error occurred during the test: $_"
# Retrieve the description from the test definitions
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
} }
end { end {
# Return auditResults # Return the audit result
return $auditResults return $auditResult
} }
} }

View File

@@ -1,70 +1,140 @@
function Test-RestrictCustomScripts { function Test-RestrictCustomScripts {
[CmdletBinding()] [CmdletBinding()]
[OutputType([CISAuditResult])]
param ( param (
# Define your parameters here if needed # Define your parameters here if needed
) )
#Limit All
begin {
# .TODO Test behavior in Prod
# Dot source the class script
$auditResults = @() begin {
# Dot source the class script if necessary
# . .\source\Classes\CISAuditResult.ps1
# Initialization code, if needed
$recnum = "7.3.4"
} }
process { process {
# CIS 2.7 Ensure custom script execution is restricted on site collections try {
# Pass if DenyAddAndCustomizePages is set to true (Enabled). Fail otherwise. # 7.3.4 (L1) Ensure custom script execution is restricted on site collections
#
# Validate test for a pass:
# - Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
# - Specific conditions to check:
# - Condition A: The `DenyAddAndCustomizePages` setting is set to `Enabled` for each site collection.
# - Condition B: The setting is validated through PowerShell commands ensuring the correct state.
# - Condition C: Verification using the SharePoint Admin Center confirms the `DenyAddAndCustomizePages` setting is enforced.
#
# Validate test for a fail:
# - Confirm that the failure conditions in the automated test are consistent with the manual audit results.
# - Specific conditions to check:
# - Condition A: The `DenyAddAndCustomizePages` setting is not set to `Enabled` for any site collection.
# - Condition B: The setting is not validated through PowerShell commands, indicating misconfiguration.
# - Condition C: Verification using the SharePoint Admin Center indicates that the `DenyAddAndCustomizePages` setting is not enforced.
# Get all site collections and select necessary properties # Retrieve all site collections and select necessary properties
$SPOSitesCustomScript = Get-SPOSite -Limit All | Select-Object Title, Url, DenyAddAndCustomizePages $SPOSitesCustomScript = Get-SPOSite -Limit All | Select-Object Title, Url, DenyAddAndCustomizePages
# Find sites where custom scripts are allowed (DenyAddAndCustomizePages is not 'Enabled') # Process URLs to replace 'sharepoint.com' with '<SPUrl>'
$customScriptAllowedSites = $SPOSitesCustomScript | Where-Object { $_.DenyAddAndCustomizePages -ne 'Enabled' } $processedUrls = $SPOSitesCustomScript | ForEach-Object {
$_.Url = $_.Url -replace 'sharepoint\.com', '<SPUrl>'
$_
}
# Compliance is true if no sites allow custom scripts # Find sites where custom scripts are allowed
$complianceResult = $customScriptAllowedSites.Count -eq 0 $customScriptAllowedSites = $processedUrls | Where-Object { $_.DenyAddAndCustomizePages -ne 'Enabled' }
#$verbosePreference = 'Continue'
# Check the total length of URLs
$totalUrlLength = ($customScriptAllowedSites.Url -join '').Length
Write-Verbose "Total length of URLs: $totalUrlLength"
# Gather details for non-compliant sites (where custom scripts are allowed) # Extract hostnames from allowed sites if the total length exceeds the limit
$nonCompliantSiteDetails = $customScriptAllowedSites | ForEach-Object { $mostUsedHostname = $null
"$($_.Title) ($($_.Url)): Custom Script Allowed" if ($totalUrlLength -gt 20000) {
Write-Verbose "Extracting hostnames from URLs..."
$hostnames = $customScriptAllowedSites.Url | ForEach-Object {
if ($_ -match '^https://([^\.]+)\.') {
$matches[1]
}
}
Write-Verbose "Extracted hostnames: $($hostnames -join ', ')"
# Find the most used hostname using the Get-MostCommonWord function
$mostUsedHostname = Get-MostCommonWord -InputStrings $hostnames
Write-Verbose "Most used hostname: $mostUsedHostname"
}
#$verbosePreference = 'SilentlyContinue'
# Compliance is true if no sites allow custom scripts
$complianceResult = $customScriptAllowedSites.Count -eq 0
# Gather details for non-compliant sites (where custom scripts are allowed)
$nonCompliantSiteDetails = $customScriptAllowedSites | ForEach-Object {
$url = $_.Url
if ($null -ne $mostUsedHostname -and $url -match "^https://$mostUsedHostname\.<SPUrl>") {
$url = $url -replace "^https://$mostUsedHostname\.<SPUrl>", "https://<corp>.<SPUrl>"
}
"$(if ($_.Title) {$_.Title} else {"NoTitle"})|$url"
}
# Prepare failure reasons and details based on compliance
$failureReasons = if (-not $complianceResult) {
"Some site collections are not restricting custom script execution. Review Details property for sites that are not aligned with the benchmark."
}
else {
"N/A"
}
$details = if ($complianceResult) {
"All site collections have custom script execution restricted"
}
else {
"Title|Url`n" + ($nonCompliantSiteDetails -join "`n")
}
# Convert details to PSObject and check length
$detailsPSObject = $details | ConvertFrom-Csv -Delimiter '|'
$detailsLength = ($detailsPSObject | ForEach-Object { $_.Url }).Length
if ($detailsLength -gt 32767) {
# Create a preview of the first 10 results
$preview = $detailsPSObject | Select-Object -First 10 | ForEach-Object { "$($_.Title)|$($_.Url)" }
$details = "The output is too large. Here is a preview of the first 10 results:`n`n" + ($preview -join "`n") + "`n`nPlease run the test with the following commands to get the full details:`n`nGet-SPOSite -Limit All | Where-Object { `$.DenyAddAndCustomizePages -ne 'Enabled' } | Select-Object Title, Url"
}
# Create and populate the CISAuditResult object
$params = @{
Rec = $recnum
Result = $complianceResult
Status = if ($complianceResult) { "Pass" } else { "Fail" }
Details = $details
FailureReason = $failureReasons
}
$auditResult = Initialize-CISAuditResult @params
} }
catch {
Write-Error "An error occurred during the test: $_"
# Create an instance of CISAuditResult and populate it # Retrieve the description from the test definitions
$auditResult = [CISAuditResult]::new() $testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$auditResult.CISControlVer = "v8" $description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$auditResult.CISControl = "2.7"
$auditResult.CISDescription = "Allowlist Authorized Scripts"
$auditResult.Rec = "7.3.4"
$auditResult.ELevel = "E3"
$auditResult.ProfileLevel = "L1"
$auditResult.IG1 = $false
$auditResult.IG2 = $false
$auditResult.IG3 = $true
$auditResult.RecDescription = "Ensure custom script execution is restricted on site collections"
$auditResult.Result = $complianceResult
$auditResult.Details = if (-not $complianceResult) {
$nonCompliantSiteDetails -join "; "
} else {
"All site collections have custom script execution restricted"
}
$auditResult.FailureReason = if (-not $complianceResult) {
"The following site collections allow custom script execution: " + ($nonCompliantSiteDetails -join "; ")
} else {
"N/A"
}
$auditResult.Status = if ($complianceResult) {
"Pass"
} else {
"Fail"
}
$auditResults += $auditResult $script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
} }
end { end {
# Return auditResults # Measure the character count of the details
return $auditResults #$verbosePreference = 'Continue'
$detailsLength = $details.Length
Write-Verbose "Character count of the details: $detailsLength"
if ($detailsLength -gt 32767) {
Write-Verbose "Warning: The character count exceeds the limit for Excel cells."
}
#$verbosePreference = 'SilentlyContinue'
# Return auditResult
return $auditResult
} }
} }

View File

@@ -1,37 +1,79 @@
function Test-RestrictExternalSharing { function Test-RestrictExternalSharing {
[CmdletBinding()] [CmdletBinding()]
[OutputType([CISAuditResult])]
param ( param (
# Aligned
# Define your parameters here # Define your parameters here
) )
begin { begin {
# Initialization code <#
Conditions for 7.2.3 (L1) Ensure external content sharing is restricted
$auditResult = [CISAuditResult]::new() Validate test for a pass:
- Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
- Specific conditions to check:
- Condition A: The SharingCapability is set to "ExternalUserSharingOnly" or more restrictive in the SharePoint admin center.
- Condition B: Using PowerShell, the SharingCapability property for the SharePoint tenant is set to "ExternalUserSharingOnly", "ExistingExternalUserSharingOnly", or "Disabled".
- Condition C: The external sharing settings in SharePoint Online and OneDrive are set to the same or a more restrictive level than the organizations sharing settings.
Validate test for a fail:
- Confirm that the failure conditions in the automated test are consistent with the manual audit results.
- Specific conditions to check:
- Condition A: The SharingCapability is set to "Anyone" or "ExternalUserAndGuestSharing" in the SharePoint admin center.
- Condition B: Using PowerShell, the SharingCapability property for the SharePoint tenant is set to "Anyone" or "ExternalUserAndGuestSharing".
- Condition C: The external sharing settings in SharePoint Online and OneDrive are set to a more permissive level than the organizations sharing settings.
#>
# Dot source the class script if necessary
#. .\source\Classes\CISAuditResult.ps1
# Initialization code, if needed
$recnum = "7.2.3"
} }
process { process {
# 7.2.3 (L1) Ensure external content sharing is restricted try {
$SPOTenantSharingCapability = Get-SPOTenant | Select-Object SharingCapability # 7.2.3 (L1) Ensure external content sharing is restricted
$isRestricted = $SPOTenantSharingCapability.SharingCapability -in @('ExternalUserSharingOnly', 'ExistingExternalUserSharingOnly', 'Disabled')
# Populate the auditResult object with the required properties # Retrieve the SharingCapability setting for the SharePoint tenant
$auditResult.CISControlVer = "v8" $SPOTenantSharingCapability = Get-SPOTenant | Select-Object SharingCapability
$auditResult.CISControl = "3.3" $isRestricted = $SPOTenantSharingCapability.SharingCapability -in @('ExternalUserSharingOnly', 'ExistingExternalUserSharingOnly', 'Disabled')
$auditResult.CISDescription = "Configure Data Access Control Lists"
$auditResult.Rec = "7.2.3" # Prepare failure reasons and details based on compliance
$auditResult.ELevel = "E3" # Condition B: Using PowerShell, the SharingCapability property for the SharePoint tenant is set to "ExternalUserSharingOnly", "ExistingExternalUserSharingOnly", or "Disabled".
$auditResult.ProfileLevel = "L1" $failureReasons = if (-not $isRestricted) {
$auditResult.IG1 = $true "External content sharing is not adequately restricted. Current setting: $($SPOTenantSharingCapability.SharingCapability)"
$auditResult.IG2 = $true }
$auditResult.IG3 = $true else {
$auditResult.RecDescription = "Ensure external content sharing is restricted" "N/A"
}
$auditResult.Result = $isRestricted # Condition A: The SharingCapability is set to "ExternalUserSharingOnly" or more restrictive in the SharePoint admin center.
$auditResult.Details = "SharingCapability: $($SPOTenantSharingCapability.SharingCapability)" # Condition C: The external sharing settings in SharePoint Online and OneDrive are set to the same or a more restrictive level than the organizations sharing settings.
$auditResult.FailureReason = if (-not $isRestricted) { "External content sharing is not adequately restricted. Current setting: $($SPOTenantSharingCapability.SharingCapability)" } else { "N/A" } $details = "SharingCapability: $($SPOTenantSharingCapability.SharingCapability)"
$auditResult.Status = if ($isRestricted) { "Pass" } else { "Fail" }
# Create and populate the CISAuditResult object
$params = @{
Rec = $recnum
Result = $isRestricted
Status = if ($isRestricted) { "Pass" } else { "Fail" }
Details = $details
FailureReason = $failureReasons
}
$auditResult = Initialize-CISAuditResult @params
}
catch {
Write-Error "An error occurred during the test: $_"
# Retrieve the description from the test definitions
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
} }
end { end {

View File

@@ -1,89 +1,113 @@
function Test-RestrictOutlookAddins { function Test-RestrictOutlookAddins {
[CmdletBinding()] [CmdletBinding()]
[OutputType([CISAuditResult])]
param ( param (
# Aligned
# Parameters could include credentials or other necessary data # Parameters could include credentials or other necessary data
) )
begin { begin {
# Initialization code # Dot source the class script if necessary
#. .\source\Classes\CISAuditResult.ps1
$auditResult = [CISAuditResult]::new() # Initialization code
$customPolicyFailures = @() $customPolicyFailures = @()
$defaultPolicyFailureDetails = @() $defaultPolicyFailureDetails = @()
$relevantRoles = @('My Custom Apps', 'My Marketplace Apps', 'My ReadWriteMailbox Apps') $relevantRoles = @('My Custom Apps', 'My Marketplace Apps', 'My ReadWriteMailbox Apps')
$recnum = "6.3.1"
# Conditions for 6.3.1 (L2) Ensure users installing Outlook add-ins is not allowed
#
# Validate test for a pass:
# - Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
# - Specific conditions to check:
# - Condition A: Verify that the roles MyCustomApps, MyMarketplaceApps, and MyReadWriteMailboxApps are unchecked under Other roles.
# - Condition B: Using PowerShell, verify that MyCustomApps, MyMarketplaceApps, and MyReadWriteMailboxApps are not assigned to users.
#
# Validate test for a fail:
# - Confirm that the failure conditions in the automated test are consistent with the manual audit results.
# - Specific conditions to check:
# - Condition A: One or more of the roles MyCustomApps, MyMarketplaceApps, and MyReadWriteMailboxApps are checked under Other roles.
# - Condition B: Using PowerShell, verify that MyCustomApps, MyMarketplaceApps, and MyReadWriteMailboxApps are assigned to users.
} }
process { process {
# Main functionality try {
# 6.3.1 (L2) Ensure users installing Outlook add-ins is not allowed # 6.3.1 (L2) Ensure users installing Outlook add-ins is not allowed
# Check all mailboxes for custom policies with unallowed add-ins # Check all mailboxes for custom policies with unallowed add-ins
$roleAssignmentPolicies = Get-EXOMailbox | Select-Object -Unique RoleAssignmentPolicy $roleAssignmentPolicies = Get-EXOMailbox | Select-Object -Unique RoleAssignmentPolicy
if ($roleAssignmentPolicies.RoleAssignmentPolicy) { if ($roleAssignmentPolicies.RoleAssignmentPolicy) {
foreach ($policy in $roleAssignmentPolicies) { foreach ($policy in $roleAssignmentPolicies) {
if ($policy.RoleAssignmentPolicy) { if ($policy.RoleAssignmentPolicy) {
$rolePolicyDetails = Get-RoleAssignmentPolicy -Identity $policy.RoleAssignmentPolicy $rolePolicyDetails = Get-RoleAssignmentPolicy -Identity $policy.RoleAssignmentPolicy
$foundRoles = $rolePolicyDetails.AssignedRoles | Where-Object { $_ -in $relevantRoles } $foundRoles = $rolePolicyDetails.AssignedRoles | Where-Object { $_ -in $relevantRoles }
if ($foundRoles) {
$customPolicyFailures += "Policy: $($policy.RoleAssignmentPolicy): Roles: $($foundRoles -join ', ')" # Condition B: Using PowerShell, verify that MyCustomApps, MyMarketplaceApps, and MyReadWriteMailboxApps are not assigned to users.
if ($foundRoles) {
$customPolicyFailures += "Policy: $($policy.RoleAssignmentPolicy): Roles: $($foundRoles -join ', ')"
}
} }
} }
} }
}
# Check Default Role Assignment Policy # Check Default Role Assignment Policy
$defaultPolicy = Get-RoleAssignmentPolicy "Default Role Assignment Policy" $defaultPolicy = Get-RoleAssignmentPolicy "Default Role Assignment Policy"
$defaultPolicyRoles = $defaultPolicy.AssignedRoles | Where-Object { $_ -in $relevantRoles } $defaultPolicyRoles = $defaultPolicy.AssignedRoles | Where-Object { $_ -in $relevantRoles }
if ($defaultPolicyRoles) {
$defaultPolicyFailureDetails = $defaultPolicyRoles # Condition A: Verify that the roles MyCustomApps, MyMarketplaceApps, and MyReadWriteMailboxApps are unchecked under Other roles.
if ($defaultPolicyRoles) {
$defaultPolicyFailureDetails = $defaultPolicyRoles
}
# Prepare result details string
$detailsString = ""
if ($customPolicyFailures) {
$detailsString += "Custom Policy Failures: | "
$detailsString += ($customPolicyFailures -join " | ")
}
else {
$detailsString += "Custom Policy Failures: None | "
}
$detailsString += "Default Role Assignment Policy: "
if ($defaultPolicyFailureDetails) {
$detailsString += ($defaultPolicyFailureDetails -join ', ')
}
else {
$detailsString += "Compliant"
}
# Determine result based on findings
$isCompliant = -not ($customPolicyFailures -or $defaultPolicyFailureDetails)
# Create and populate the CISAuditResult object
$params = @{
Rec = $recnum
Result = $isCompliant
Status = if ($isCompliant) { "Pass" } else { "Fail" }
Details = $detailsString
FailureReason = if ($isCompliant) { "N/A" } else { "Unauthorized Outlook add-ins found in custom or default policies." }
}
$auditResult = Initialize-CISAuditResult @params
}
catch {
Write-Error "An error occurred during the test: $_"
# Retrieve the description from the test definitions
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
} }
} }
end { end {
# Prepare result object # Return the audit result
$auditResult.Rec = "6.3.1"
$auditResult.CISControl = "9.4"
$auditResult.CISDescription = "Restrict Unnecessary or Unauthorized Browser and Email Client Extensions"
$auditResult.ELevel = "E3"
$auditResult.ProfileLevel = "L2"
$auditResult.IG1 = $false
$auditResult.IG2 = $true
$auditResult.IG3 = $true
$auditResult.RecDescription = "Ensure users installing Outlook add-ins is not allowed"
$detailsString = ""
if ($customPolicyFailures) {
$detailsString += "Custom Policy Failures: | "
# Use pipes or tabs here instead of newlines
$detailsString += ($customPolicyFailures -join " | ")
}
else {
$detailsString += "Custom Policy Failures: None | "
}
$detailsString += "Default Role Assignment Policy: "
if ($defaultPolicyFailureDetails) {
$detailsString += ($defaultPolicyFailureDetails -join ', ')
}
else {
$detailsString += "Compliant"
}
if ($customPolicyFailures -or $defaultPolicyFailureDetails) {
$auditResult.Result = $false
$auditResult.Status = "Fail"
$auditResult.Details = $detailsString
$auditResult.FailureReason = "Unauthorized Outlook add-ins found in custom or default policies."
}
else {
$auditResult.Result = $true
$auditResult.Status = "Pass"
$auditResult.Details = "No unauthorized Outlook add-ins found in custom or default policies."
$auditResult.FailureReason = "N/A"
}
# Return auditResult
return $auditResult return $auditResult
} }
} }

View File

@@ -1,48 +1,87 @@
function Test-RestrictStorageProvidersOutlook { function Test-RestrictStorageProvidersOutlook {
[CmdletBinding()] [CmdletBinding()]
[OutputType([CISAuditResult])]
param ( param (
# Aligned
# Parameters can be added here if needed # Parameters can be added here if needed
) )
begin { begin {
# Dot source the class script <#
# 6.5.3 (L2) Ensure additional storage providers are restricted in Outlook on the web
#
# Validate test for a pass:
# - Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
# - Specific conditions to check:
# - Condition A: Using PowerShell, verify that `AdditionalStorageProvidersAvailable` is set to `False` in the OwaMailboxPolicy.
# - Condition B: Ensure that the command `Get-OwaMailboxPolicy | Format-Table Name, AdditionalStorageProvidersAvailable` returns `False`.
#
# Validate test for a fail:
# - Confirm that the failure conditions in the automated test are consistent with the manual audit results.
# - Specific conditions to check:
# - Condition A: Using PowerShell, verify that `AdditionalStorageProvidersAvailable` is not set to `False` in the OwaMailboxPolicy.
# - Condition B: Ensure that the command `Get-OwaMailboxPolicy | Format-Table Name, AdditionalStorageProvidersAvailable` does not return `False`.
#>
$auditResult = [CISAuditResult]::new() # Dot source the class script if necessary
#. .\source\Classes\CISAuditResult.ps1
# Initialization code, if needed
$recnum = "6.5.3"
} }
process { process {
# 6.5.3 (L2) Ensure additional storage providers are restricted in Outlook on the web try {
$owaPolicies = Get-OwaMailboxPolicy # 6.5.3 (L2) Ensure additional storage providers are restricted in Outlook on the web
$allPoliciesRestricted = $owaPolicies | ForEach-Object { $_.AdditionalStorageProvidersAvailable } | ForEach-Object { -not $_ }
# Create an instance of CISAuditResult and populate it # Retrieve all OwaMailbox policies
$auditResult.CISControlVer = "v8" $owaPolicies = Get-OwaMailboxPolicy
$auditResult.CISControl = "3.3" # Condition A: Check if AdditionalStorageProvidersAvailable is set to False
$auditResult.CISDescription = "Configure Data Access Control Lists" $nonCompliantPolicies = $owaPolicies | Where-Object { $_.AdditionalStorageProvidersAvailable }
$auditResult.Rec = "6.5.3"
$auditResult.ELevel = "E3" # Based on your environment # Determine compliance
$auditResult.ProfileLevel = "L2" $allPoliciesRestricted = $nonCompliantPolicies.Count -eq 0
$auditResult.IG1 = $true
$auditResult.IG2 = $true # Prepare failure reasons and details based on compliance
$auditResult.IG3 = $true $failureReasons = if ($allPoliciesRestricted) {
$auditResult.RecDescription = "Ensure additional storage providers are restricted in Outlook on the web" "N/A"
$auditResult.Result = $allPoliciesRestricted }
$auditResult.Details = if($allPoliciesRestricted) { else {
"All OwaMailbox policies restrict AdditionalStorageProvidersAvailable" "One or more OwaMailbox policies allow AdditionalStorageProvidersAvailable."
} else { }
$nonCompliantPolicies = $owaPolicies | Where-Object { $_.AdditionalStorageProvidersAvailable } | Select-Object -ExpandProperty Name
"Non-compliant OwaMailbox policies: $($nonCompliantPolicies -join ', ')" $details = if ($allPoliciesRestricted) {
"All OwaMailbox policies restrict AdditionalStorageProvidersAvailable"
}
else {
"Non-compliant OwaMailbox policies: $($nonCompliantPolicies.Name -join ', ')"
}
# Create and populate the CISAuditResult object
$params = @{
Rec = $recnum
Result = $allPoliciesRestricted
Status = if ($allPoliciesRestricted) { "Pass" } else { "Fail" }
Details = $details
FailureReason = $failureReasons
}
$auditResult = Initialize-CISAuditResult @params
}
catch {
Write-Error "An error occurred during the test: $_"
# Retrieve the description from the test definitions
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
} }
$auditResult.FailureReason = if(-not $allPoliciesRestricted) { "One or more OwaMailbox policies allow AdditionalStorageProvidersAvailable." } else { "N/A" }
$auditResult.Status = if($allPoliciesRestricted) { "Pass" } else { "Fail" }
} }
end { end {
# Return auditResult # Return the audit result
return $auditResult return $auditResult
} }
} }
# Additional helper functions (if any)

View File

@@ -1,43 +1,79 @@
function Test-RestrictTenantCreation { function Test-RestrictTenantCreation {
[CmdletBinding()] [CmdletBinding()]
[OutputType([CISAuditResult])]
param ( param (
# Aligned
# Parameters can be added if needed # Parameters can be added if needed
) )
begin { begin {
# Dot source the class script # Dot source the class script if necessary
#. .\source\Classes\CISAuditResult.ps1
$auditResults = @() # Initialization code, if needed
$recnum = "5.1.2.3"
<#
Conditions for 5.1.2.3 (L1) Ensure 'Restrict non-admin users from creating tenants' is set to 'Yes'
Validate test for a pass:
- Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
- Specific conditions to check:
- Condition A: Restrict non-admin users from creating tenants is set to 'Yes' in the Azure AD and Entra administration portal.
- Condition B: Using PowerShell, the setting for restricting non-admin users from creating tenants is set to 'Yes'.
Validate test for a fail:
- Confirm that the failure conditions in the automated test are consistent with the manual audit results.
- Specific conditions to check:
- Condition A: Restrict non-admin users from creating tenants is not set to 'Yes' in the Azure AD and Entra administration portal.
- Condition B: Using PowerShell, the setting for restricting non-admin users from creating tenants is not set to 'Yes'.
#>
} }
process { process {
# 5.1.2.3 (L1) Ensure 'Restrict non-admin users from creating tenants' is set to 'Yes' try {
# Pass if AllowedToCreateTenants is False. Fail otherwise. # 5.1.2.3 (L1) Ensure 'Restrict non-admin users from creating tenants' is set to 'Yes'
$tenantCreationPolicy = (Get-MgPolicyAuthorizationPolicy).DefaultUserRolePermissions | Select-Object AllowedToCreateTenants
$tenantCreationResult = -not $tenantCreationPolicy.AllowedToCreateTenants
# Create an instance of CISAuditResult and populate it # Retrieve the tenant creation policy
$auditResult = [CISAuditResult]::new() $tenantCreationPolicy = (Get-MgPolicyAuthorizationPolicy).DefaultUserRolePermissions | Select-Object AllowedToCreateTenants
$auditResult.Status = if ($tenantCreationResult) { "Pass" } else { "Fail" } $tenantCreationResult = -not $tenantCreationPolicy.AllowedToCreateTenants
$auditResult.ELevel = "E3"
$auditResult.ProfileLevel = "L1"
$auditResult.Rec = "5.1.2.3"
$auditResult.RecDescription = "Ensure 'Restrict non-admin users from creating tenants' is set to 'Yes'"
$auditResult.CISControlVer = "v8"
$auditResult.CISControl = "0.0"
$auditResult.CISDescription = "Explicitly Not Mapped"
$auditResult.IG1 = $false
$auditResult.IG2 = $false
$auditResult.IG3 = $false
$auditResult.Result = $tenantCreationResult
$auditResult.Details = "AllowedToCreateTenants: $($tenantCreationPolicy.AllowedToCreateTenants)"
$auditResult.FailureReason = if (-not $tenantCreationResult) { "Non-admin users can create tenants" } else { "N/A" }
$auditResults += $auditResult # Prepare failure reasons and details based on compliance
$failureReasons = if ($tenantCreationResult) {
"N/A"
}
else {
"Non-admin users can create tenants"
}
$details = "AllowedToCreateTenants: $($tenantCreationPolicy.AllowedToCreateTenants)"
# Create and populate the CISAuditResult object
$params = @{
Rec = $recnum
Result = $tenantCreationResult
Status = if ($tenantCreationResult) { "Pass" } else { "Fail" }
Details = $details
FailureReason = $failureReasons
}
$auditResult = Initialize-CISAuditResult @params
}
catch {
Write-Error "An error occurred during the test: $_"
# Retrieve the description from the test definitions
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
} }
end { end {
# Return auditResults # Return the audit result
return $auditResults return $auditResult
} }
} }

View File

@@ -1,49 +1,93 @@
function Test-SafeAttachmentsPolicy { function Test-SafeAttachmentsPolicy {
[CmdletBinding()] [CmdletBinding()]
[OutputType([CISAuditResult])]
param ( param (
# Aligned
# Parameters can be added if needed # Parameters can be added if needed
) )
begin { begin {
# Dot source the class script if necessary
#. .\source\Classes\CISAuditResult.ps1
$auditResults = @() # Initialization code, if needed
$recnum = "2.1.4"
<#
Conditions for 2.1.4 (L2) Ensure Safe Attachments policy is enabled
Validate test for a pass:
- Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
- Specific conditions to check:
- Condition A: The Safe Attachments policy is enabled in the Microsoft 365 Defender portal.
- Condition B: The policy covers all recipients within the organization.
- Condition C: The policy action is set to "Dynamic Delivery" or "Quarantine".
- Condition D: The policy is not disabled.
Validate test for a fail:
- Confirm that the failure conditions in the automated test are consistent with the manual audit results.
- Specific conditions to check:
- Condition A: The Safe Attachments policy is not enabled in the Microsoft 365 Defender portal.
- Condition B: The policy does not cover all recipients within the organization.
- Condition C: The policy action is not set to "Dynamic Delivery" or "Quarantine".
- Condition D: The policy is disabled.
#>
} }
process { process {
# Retrieve all Safe Attachment policies where Enable is set to True try {
$safeAttachmentPolicies = Get-SafeAttachmentPolicy | Where-Object { $_.Enable -eq $true } # 2.1.4 (L2) Ensure Safe Attachments policy is enabled
# If there are any enabled policies, the result is Pass. If not, it's Fail. # Retrieve all Safe Attachment policies where Enable is set to True
$result = $safeAttachmentPolicies -ne $null -and $safeAttachmentPolicies.Count -gt 0 $safeAttachmentPolicies = Get-SafeAttachmentPolicy | Where-Object { $_.Enable -eq $true }
$details = if ($result) {
"Enabled Safe Attachments Policies: $($safeAttachmentPolicies.Name -join ', ')" # Condition A: Check if any Safe Attachments policy is enabled
} else { $result = $null -ne $safeAttachmentPolicies -and $safeAttachmentPolicies.Count -gt 0
"No Safe Attachments Policies are enabled."
# Condition B, C, D: Additional checks can be added here if more detailed policy attributes are required
# Determine details and failure reasons based on the presence of enabled policies
$details = if ($result) {
"Enabled Safe Attachments Policies: $($safeAttachmentPolicies.Name -join ', ')"
}
else {
"No Safe Attachments Policies are enabled."
}
$failureReasons = if ($result) {
"N/A"
}
else {
"Safe Attachments policy is not enabled."
}
# Create and populate the CISAuditResult object
$params = @{
Rec = $recnum
Result = $result
Status = if ($result) { "Pass" } else { "Fail" }
Details = $details
FailureReason = $failureReasons
}
$auditResult = Initialize-CISAuditResult @params
} }
$failureReason = if ($result) { "N/A" } else { "Safe Attachments policy is not enabled." } catch {
Write-Error "An error occurred during the test: $_"
# Create an instance of CISAuditResult and populate it # Retrieve the description from the test definitions
$auditResult = [CISAuditResult]::new() $testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$auditResult.Status = if ($result) { "Pass" } else { "Fail" } $description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$auditResult.ELevel = "E5"
$auditResult.ProfileLevel = "L2"
$auditResult.Rec = "2.1.4"
$auditResult.RecDescription = "Ensure Safe Attachments policy is enabled"
$auditResult.CISControlVer = "v8"
$auditResult.CISControl = "9.7"
$auditResult.CISDescription = "Deploy and Maintain Email Server Anti-Malware Protections"
$auditResult.IG1 = $false
$auditResult.IG2 = $false
$auditResult.IG3 = $true
$auditResult.Result = $result
$auditResult.Details = $details
$auditResult.FailureReason = $failureReason
$auditResults += $auditResult $script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
} }
end { end {
# Return auditResults # Return the audit result
return $auditResults return $auditResult
} }
} }

View File

@@ -1,57 +1,95 @@
function Test-SafeAttachmentsTeams { function Test-SafeAttachmentsTeams {
[CmdletBinding()] [CmdletBinding()]
[OutputType([CISAuditResult])]
param ( param (
# Aligned
# Parameters can be added if needed # Parameters can be added if needed
) )
begin { begin {
# Dot source the class script if necessary
#. .\source\Classes\CISAuditResult.ps1
$auditResults = @() # Conditions for 2.1.5 (L2) Ensure Safe Attachments for SharePoint, OneDrive, and Microsoft Teams is Enabled
#
# Validate test for a pass:
# - Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
# - Specific conditions to check:
# - Condition A: Safe Attachments for SharePoint is enabled.
# - Condition B: Safe Attachments for OneDrive is enabled.
# - Condition C: Safe Attachments for Microsoft Teams is enabled.
#
# Validate test for a fail:
# - Confirm that the failure conditions in the automated test are consistent with the manual audit results.
# - Specific conditions to check:
# - Condition A: Safe Attachments for SharePoint is not enabled.
# - Condition B: Safe Attachments for OneDrive is not enabled.
# - Condition C: Safe Attachments for Microsoft Teams is not enabled.
# Initialization code, if needed
$recnum = "2.1.5"
} }
process { process {
# Requires E5 license try {
# Retrieve the ATP policies for Office 365 and check Safe Attachments settings # 2.1.5 (L2) Ensure Safe Attachments for SharePoint, OneDrive, and Microsoft Teams is Enabled
$atpPolicies = Get-AtpPolicyForO365
# Check if the required ATP policies are enabled # Retrieve the ATP policies for Office 365 and check Safe Attachments settings
$atpPolicyResult = $atpPolicies | Where-Object { $atpPolicies = Get-AtpPolicyForO365
$_.EnableATPForSPOTeamsODB -eq $true -and
$_.EnableSafeDocs -eq $true -and # Check if the required ATP policies are enabled
$_.AllowSafeDocsOpen -eq $false $atpPolicyResult = $atpPolicies | Where-Object {
$_.EnableATPForSPOTeamsODB -eq $true -and
$_.EnableSafeDocs -eq $true -and
$_.AllowSafeDocsOpen -eq $false
}
# Condition A: Check Safe Attachments for SharePoint
# Condition B: Check Safe Attachments for OneDrive
# Condition C: Check Safe Attachments for Microsoft Teams
# Determine the result based on the ATP policy settings
$result = $null -ne $atpPolicyResult
$details = if ($result) {
"ATP for SharePoint, OneDrive, and Teams is enabled with correct settings."
}
else {
"ATP for SharePoint, OneDrive, and Teams is not enabled with correct settings."
}
$failureReasons = if ($result) {
"N/A"
}
else {
"ATP policy for SharePoint, OneDrive, and Microsoft Teams is not correctly configured."
}
# Create and populate the CISAuditResult object
$params = @{
Rec = $recnum
Result = $result
Status = if ($result) { "Pass" } else { "Fail" }
Details = $details
FailureReason = $failureReasons
}
$auditResult = Initialize-CISAuditResult @params
} }
catch {
Write-Error "An error occurred during the test: $_"
# Determine the result based on the ATP policy settings # Retrieve the description from the test definitions
$result = $null -ne $atpPolicyResult $testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$details = if ($result) { $description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
"ATP for SharePoint, OneDrive, and Teams is enabled with correct settings."
} else { $script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
"ATP for SharePoint, OneDrive, and Teams is not enabled with correct settings."
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
} }
$failureReason = if ($result) { "N/A" } else { "ATP policy for SharePoint, OneDrive, and Microsoft Teams is not correctly configured." }
# Create an instance of CISAuditResult and populate it
$auditResult = [CISAuditResult]::new()
$auditResult.Status = if ($result) { "Pass" } else { "Fail" }
$auditResult.ELevel = "E5"
$auditResult.ProfileLevel = "L2"
$auditResult.Rec = "2.1.5"
$auditResult.RecDescription = "Ensure Safe Attachments for SharePoint, OneDrive, and Microsoft Teams is Enabled"
$auditResult.CISControlVer = "v8"
$auditResult.CISControl = "9.7, 10.1"
$auditResult.CISDescription = "Deploy and Maintain Email Server Anti-Malware Protections, Deploy and Maintain Anti-Malware Software"
$auditResult.IG1 = $true
$auditResult.IG2 = $true
$auditResult.IG3 = $true
$auditResult.Result = $result
$auditResult.Details = $details
$auditResult.FailureReason = $failureReason
$auditResults += $auditResult
} }
end { end {
# Return auditResults # Return the audit result
return $auditResults return $auditResult
} }
} }

View File

@@ -1,70 +1,105 @@
function Test-SafeLinksOfficeApps { function Test-SafeLinksOfficeApps {
[CmdletBinding()] [CmdletBinding()]
[OutputType([CISAuditResult])]
param ( param (
# Aligned
# Define your parameters here if needed # Define your parameters here if needed
) )
begin { begin {
# Initialization code # Dot source the class script if necessary
#. .\source\Classes\CISAuditResult.ps1
# Initialization code, if needed
$recnum = "2.1.1"
$auditResults = @() <#
Conditions for 2.1.1 (L2) Ensure Safe Links for Office Applications is Enabled
Validate test for a pass:
- Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
- Specific conditions to check:
- Condition A: In the Microsoft 365 security center, Safe Links policy for Office applications is enabled and the following protection settings are set:
- Office 365 Apps: On
- Teams: On
- Email: On
- Click protection settings: On
- Do not track when users click safe links: Off
- Condition B: Using the Exchange Online PowerShell Module, Safe Links policies are retrieved, and the relevant policy shows Safe Links for Office applications is enabled.
Validate test for a fail:
- Confirm that the failure conditions in the automated test are consistent with the manual audit results.
- Specific conditions to check:
- Condition A: In the Microsoft 365 security center, Safe Links policy for Office applications is not enabled or one or more of the required protection settings are not set correctly.
- Office 365 Apps: Off
- Teams: Off
- Email: Off
- Click protection settings: Off
- Do not track when users click safe links: On
- Condition B: Using the Exchange Online PowerShell Module, Safe Links policies are retrieved, and the relevant policy shows Safe Links for Office applications is not enabled.
#>
} }
process { process {
# Retrieve all Safe Links policies try {
$policies = Get-SafeLinksPolicy # 2.1.1 (L2) Ensure Safe Links for Office Applications is Enabled
# Initialize the details collection # Retrieve all Safe Links policies
$misconfiguredDetails = @() $policies = Get-SafeLinksPolicy
foreach ($policy in $policies) { # Initialize the details collection
# Get the detailed configuration of each policy $misconfiguredDetails = @()
$policyDetails = Get-SafeLinksPolicy -Identity $policy.Name
# Check each required property and record failures foreach ($policy in $policies) {
$failures = @() # Get the detailed configuration of each policy
if ($policyDetails.EnableSafeLinksForEmail -ne $true) { $failures += "EnableSafeLinksForEmail: False" } $policyDetails = Get-SafeLinksPolicy -Identity $policy.Name
if ($policyDetails.EnableSafeLinksForTeams -ne $true) { $failures += "EnableSafeLinksForTeams: False" }
if ($policyDetails.EnableSafeLinksForOffice -ne $true) { $failures += "EnableSafeLinksForOffice: False" }
if ($policyDetails.TrackClicks -ne $true) { $failures += "TrackClicks: False" }
if ($policyDetails.AllowClickThrough -ne $false) { $failures += "AllowClickThrough: True" }
if ($policyDetails.ScanUrls -ne $true) { $failures += "ScanUrls: False" }
if ($policyDetails.EnableForInternalSenders -ne $true) { $failures += "EnableForInternalSenders: False" }
if ($policyDetails.DeliverMessageAfterScan -ne $true) { $failures += "DeliverMessageAfterScan: False" }
if ($policyDetails.DisableUrlRewrite -ne $false) { $failures += "DisableUrlRewrite: True" }
# Only add details for policies that have misconfigurations # Check each required property and record failures
if ($failures.Count -gt 0) { # Condition A: Checking policy settings
$misconfiguredDetails += "Policy: $($policy.Name); Failures: $($failures -join ', ')" $failures = @()
if ($policyDetails.EnableSafeLinksForEmail -ne $true) { $failures += "EnableSafeLinksForEmail: False" } # Email: On
if ($policyDetails.EnableSafeLinksForTeams -ne $true) { $failures += "EnableSafeLinksForTeams: False" } # Teams: On
if ($policyDetails.EnableSafeLinksForOffice -ne $true) { $failures += "EnableSafeLinksForOffice: False" } # Office 365 Apps: On
if ($policyDetails.TrackClicks -ne $true) { $failures += "TrackClicks: False" } # Click protection settings: On
if ($policyDetails.AllowClickThrough -ne $false) { $failures += "AllowClickThrough: True" } # Do not track when users click safe links: Off
# Only add details for policies that have misconfigurations
if ($failures.Count -gt 0) {
$misconfiguredDetails += "Policy: $($policy.Name); Failures: $($failures -join ', ')"
}
} }
# Prepare the final result
# Condition B: Ensuring no misconfigurations
$result = $misconfiguredDetails.Count -eq 0
$details = if ($result) { "All Safe Links policies are correctly configured." } else { $misconfiguredDetails -join ' | ' }
$failureReasons = if ($result) { "N/A" } else { "The following Safe Links policies settings do not meet the recommended configuration: $($misconfiguredDetails -join ' | ')" }
# Create and populate the CISAuditResult object
$params = @{
Rec = $recnum
Result = $result
Status = if ($result) { "Pass" } else { "Fail" }
Details = $details
FailureReason = $failureReasons
}
$auditResult = Initialize-CISAuditResult @params
} }
catch {
Write-Error "An error occurred during the test: $_"
# Prepare the final result # Retrieve the description from the test definitions
$result = $misconfiguredDetails.Count -eq 0 $testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$details = if ($result) { "All Safe Links policies are correctly configured." } else { $misconfiguredDetails -join ' | ' } $description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
# Create the audit result object $script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
$auditResult = [CISAuditResult]::new()
$auditResult.Status = if ($result) { "Pass" } else { "Fail" }
$auditResult.ELevel = "E5"
$auditResult.ProfileLevel = "L2"
$auditResult.Rec = "2.1.1"
$auditResult.RecDescription = "Ensure Safe Links for Office Applications is Enabled"
$auditResult.CISControlVer = "v8"
$auditResult.CISControl = "10.1"
$auditResult.CISDescription = "Deploy and Maintain Anti-Malware Software"
$auditResult.IG1 = $true
$auditResult.IG2 = $true
$auditResult.IG3 = $true
$auditResult.Result = $result
$auditResult.Details = $details
$auditResult.FailureReason = if ($result) { "N/A" } else { "The following Safe Links policies settings do not meet the recommended configuration: $($misconfiguredDetails -join ' | ')" }
$auditResults += $auditResult # Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
} }
end { end {
# Return auditResults # Return the audit result
return $auditResults return $auditResult
} }
} }

View File

@@ -1,36 +1,62 @@
function Test-SharePointAADB2B { function Test-SharePointAADB2B {
[CmdletBinding()] [CmdletBinding()]
[OutputType([CISAuditResult])]
param ( param (
# Aligned
# Define your parameters here # Define your parameters here
) )
begin { begin {
# Initialization code # Conditions for 7.2.2 (L1) Ensure SharePoint and OneDrive integration with Azure AD B2B is enabled
#
# Validate test for a pass:
# - Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
# - Specific conditions to check:
# - Condition A: Ensure the `EnableAzureADB2BIntegration` property is set to `True` for the SharePoint tenant.
# - Condition B: Verify that the SharePoint and OneDrive integration with Azure AD B2B is active.
# - Condition C: Ensure that guest accounts are managed in Azure AD and subject to access policies.
#
# Validate test for a fail:
# - Confirm that the failure conditions in the automated test are consistent with the manual audit results.
# - Specific conditions to check:
# - Condition A: The `EnableAzureADB2BIntegration` property is set to `False` for the SharePoint tenant.
# - Condition B: The integration between SharePoint, OneDrive, and Azure AD B2B is not active.
# - Condition C: Guest accounts are not managed in Azure AD and are not subject to access policies.
$auditResult = [CISAuditResult]::new() # Dot source the class script if necessary
#. .\source\Classes\CISAuditResult.ps1
# Initialization code, if needed
$recnum = "7.2.2"
} }
process { process {
# 7.2.2 (L1) Ensure SharePoint and OneDrive integration with Azure AD B2B is enabled try {
$SPOTenantAzureADB2B = Get-SPOTenant | Select-Object EnableAzureADB2BIntegration # 7.2.2 (L1) Ensure SharePoint and OneDrive integration with Azure AD B2B is enabled
$SPOTenantAzureADB2B = Get-SPOTenant | Select-Object EnableAzureADB2BIntegration
# Populate the auditResult object with the required properties # Populate the auditResult object with the required properties
$auditResult.CISControlVer = "v8" $params = @{
$auditResult.CISControl = "0.0" Rec = $recnum
$auditResult.CISDescription = "Explicitly Not Mapped" Result = $SPOTenantAzureADB2B.EnableAzureADB2BIntegration
Status = if ($SPOTenantAzureADB2B.EnableAzureADB2BIntegration) { "Pass" } else { "Fail" }
Details = "EnableAzureADB2BIntegration: $($SPOTenantAzureADB2B.EnableAzureADB2BIntegration)"
FailureReason = if (-not $SPOTenantAzureADB2B.EnableAzureADB2BIntegration) { "Azure AD B2B integration is not enabled" } else { "N/A" }
}
$auditResult = Initialize-CISAuditResult @params
}
catch {
Write-Error "An error occurred during the test: $_"
$auditResult.Rec = "7.2.2" # Retrieve the description from the test definitions
$auditResult.ELevel = "E3" $testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$auditResult.ProfileLevel = "L1" $description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$auditResult.IG1 = $false
$auditResult.IG2 = $false
$auditResult.IG3 = $false
$auditResult.RecDescription = "Ensure SharePoint and OneDrive integration with Azure AD B2B is enabled"
$auditResult.Result = $SPOTenantAzureADB2B.EnableAzureADB2BIntegration $script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
$auditResult.Details = "EnableAzureADB2BIntegration: $($SPOTenantAzureADB2B.EnableAzureADB2BIntegration)"
$auditResult.FailureReason = if (-not $SPOTenantAzureADB2B.EnableAzureADB2BIntegration) { "Azure AD B2B integration is not enabled" } else { "N/A" } # Call Initialize-CISAuditResult with error parameters
$auditResult.Status = if ($SPOTenantAzureADB2B.EnableAzureADB2BIntegration) { "Pass" } else { "Fail" } $auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
} }
end { end {

View File

@@ -1,37 +1,63 @@
function Test-SharePointExternalSharingDomains { function Test-SharePointExternalSharingDomains {
[CmdletBinding()] [CmdletBinding()]
[OutputType([CISAuditResult])]
param ( param (
# Aligned
# Define your parameters here # Define your parameters here
) )
begin { begin {
# Initialization code # Dot source the class script if necessary
#. .\source\Classes\CISAuditResult.ps1
$auditResult = [CISAuditResult]::new() # Initialization code, if needed
$recnum = "7.2.6"
# Conditions for 7.2.6 (L2) Ensure SharePoint external sharing is managed through domain whitelist/blacklists
#
# Validate test for a pass:
# - Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
# - Specific conditions to check:
# - Condition A: The "Limit external sharing by domain" option is enabled in the SharePoint admin center.
# - Condition B: The "SharingDomainRestrictionMode" is set to "AllowList" using PowerShell.
# - Condition C: The "SharingAllowedDomainList" contains the domains trusted by the organization for external sharing.
#
# Validate test for a fail:
# - Confirm that the failure conditions in the automated test are consistent with the manual audit results.
# - Specific conditions to check:
# - Condition A: The "Limit external sharing by domain" option is not enabled in the SharePoint admin center.
# - Condition B: The "SharingDomainRestrictionMode" is not set to "AllowList" using PowerShell.
# - Condition C: The "SharingAllowedDomainList" does not contain the domains trusted by the organization for external sharing.
} }
process { process {
# 7.2.6 (L2) Ensure SharePoint external sharing is managed through domain whitelist/blacklists try {
$SPOTenant = Get-SPOTenant | Select-Object SharingDomainRestrictionMode, SharingAllowedDomainList # 7.2.6 (L2) Ensure SharePoint external sharing is managed through domain whitelist/blacklists
$isDomainRestrictionConfigured = $SPOTenant.SharingDomainRestrictionMode -eq 'AllowList' $SPOTenant = Get-SPOTenant | Select-Object SharingDomainRestrictionMode, SharingAllowedDomainList
$isDomainRestrictionConfigured = $SPOTenant.SharingDomainRestrictionMode -eq 'AllowList'
# Populate the auditResult object with the required properties # Populate the auditResult object with the required properties
$auditResult.CISControlVer = "v8" $params = @{
$auditResult.CISControl = "3.3" Rec = $recnum
$auditResult.CISDescription = "Configure Data Access Control Lists" Result = $isDomainRestrictionConfigured
Status = if ($isDomainRestrictionConfigured) { "Pass" } else { "Fail" }
Details = "SharingDomainRestrictionMode: $($SPOTenant.SharingDomainRestrictionMode); SharingAllowedDomainList: $($SPOTenant.SharingAllowedDomainList)"
FailureReason = if (-not $isDomainRestrictionConfigured) { "Domain restrictions for SharePoint external sharing are not configured to 'AllowList'. Current setting: $($SPOTenant.SharingDomainRestrictionMode)" } else { "N/A" }
}
$auditResult = Initialize-CISAuditResult @params
}
catch {
Write-Error "An error occurred during the test: $_"
$auditResult.Rec = "7.2.6" # Retrieve the description from the test definitions
$auditResult.ELevel = "E3" $testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$auditResult.ProfileLevel = "L2" $description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$auditResult.IG1 = $true
$auditResult.IG2 = $true
$auditResult.IG3 = $true
$auditResult.RecDescription = "Ensure SharePoint external sharing is managed through domain whitelist/blacklists"
$auditResult.Result = $isDomainRestrictionConfigured $script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
$auditResult.Details = "SharingDomainRestrictionMode: $($SPOTenant.SharingDomainRestrictionMode); SharingAllowedDomainList: $($SPOTenant.SharingAllowedDomainList)"
$auditResult.FailureReason = if (-not $isDomainRestrictionConfigured) { "Domain restrictions for SharePoint external sharing are not configured to 'AllowList'. Current setting: $($SPOTenant.SharingDomainRestrictionMode)" } else { "N/A" } # Call Initialize-CISAuditResult with error parameters
$auditResult.Status = if ($isDomainRestrictionConfigured) { "Pass" } else { "Fail" } $auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
} }
end { end {

View File

@@ -1,37 +1,63 @@
function Test-SharePointGuestsItemSharing { function Test-SharePointGuestsItemSharing {
[CmdletBinding()] [CmdletBinding()]
[OutputType([CISAuditResult])]
param ( param (
# Aligned
# Define your parameters here # Define your parameters here
) )
begin { begin {
# Initialization code # Dot source the class script if necessary
#. .\source\Classes\CISAuditResult.ps1
# Initialization code, if needed
$auditResult = [CISAuditResult]::new() $recnum = "7.2.5"
# Conditions for 7.2.5 (L2) Ensure that SharePoint guest users cannot share items they don't own
#
# Validate test for a pass:
# - Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
# - Specific conditions to check:
# - Condition A: The SharePoint setting "PreventExternalUsersFromResharing" is set to `True` using PowerShell.
# - Condition B: The SharePoint admin center setting "Allow guests to share items they don't own" is unchecked.
# - Condition C: Ensure that external users cannot re-share items they don't own.
#
# Validate test for a fail:
# - Confirm that the failure conditions in the automated test are consistent with the manual audit results.
# - Specific conditions to check:
# - Condition A: The SharePoint setting "PreventExternalUsersFromResharing" is set to `False` using PowerShell.
# - Condition B: The SharePoint admin center setting "Allow guests to share items they don't own" is checked.
# - Condition C: Ensure that external users can re-share items they don't own.
} }
process { process {
# 7.2.5 (L2) Ensure that SharePoint guest users cannot share items they don't own try {
$SPOTenant = Get-SPOTenant | Select-Object PreventExternalUsersFromResharing # 7.2.5 (L2) Ensure that SharePoint guest users cannot share items they don't own
$isGuestResharingPrevented = $SPOTenant.PreventExternalUsersFromResharing $SPOTenant = Get-SPOTenant | Select-Object PreventExternalUsersFromResharing
$isGuestResharingPrevented = $SPOTenant.PreventExternalUsersFromResharing
# Populate the auditResult object with the required properties # Populate the auditResult object with the required properties
$auditResult.CISControlVer = "v8" $params = @{
$auditResult.CISControl = "3.3" Rec = $recnum
$auditResult.CISDescription = "Configure Data Access Control Lists" Result = $isGuestResharingPrevented
Status = if ($isGuestResharingPrevented) { "Pass" } else { "Fail" }
Details = "PreventExternalUsersFromResharing: $isGuestResharingPrevented"
FailureReason = if (-not $isGuestResharingPrevented) { "Guest users can reshare items they don't own." } else { "N/A" }
}
$auditResult = Initialize-CISAuditResult @params
}
catch {
Write-Error "An error occurred during the test: $_"
$auditResult.Rec = "7.2.5" # Retrieve the description from the test definitions
$auditResult.ELevel = "E3" $testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$auditResult.ProfileLevel = "L2" $description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$auditResult.IG1 = $true
$auditResult.IG2 = $true
$auditResult.IG3 = $true
$auditResult.RecDescription = "Ensure that SharePoint guest users cannot share items they don't own"
$auditResult.Result = $isGuestResharingPrevented $script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
$auditResult.Details = "PreventExternalUsersFromResharing: $isGuestResharingPrevented"
$auditResult.FailureReason = if (-not $isGuestResharingPrevented) { "Guest users can reshare items they don't own." } else { "N/A" } # Call Initialize-CISAuditResult with error parameters
$auditResult.Status = if ($isGuestResharingPrevented) { "Pass" } else { "Fail" } $auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
} }
end { end {

View File

@@ -1,56 +1,86 @@
function Test-SpamPolicyAdminNotify { function Test-SpamPolicyAdminNotify {
[CmdletBinding()] [CmdletBinding()]
[OutputType([CISAuditResult])]
param ( param (
# Aligned
# Parameters can be added if needed # Parameters can be added if needed
) )
begin { begin {
# Dot source the class script if necessary
#. .\source\Classes\CISAuditResult.ps1
# Initialization code, if needed
$auditResults = @() $recnum = "2.1.6"
<#
Conditions for 2.1.6 (L1) Ensure Exchange Online Spam Policies are set to notify administrators
Validate recommendation details:
- Confirm that the recommendation details are accurate and complete as per the CIS benchmark.
Validate test for a pass:
- Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
- Specific conditions to check:
- Condition A: In the Microsoft 365 Security & Compliance Center, the Exchange Online Spam Policies are set to notify administrators when a sender in the organization has been blocked for sending spam emails.
- Condition B: Using PowerShell, the `NotifyOutboundSpam` and `NotifyOutboundSpamContact` properties are correctly set in all relevant spam filter policies.
Validate test for a fail:
- Confirm that the failure conditions in the automated test are consistent with the manual audit results.
- Specific conditions to check:
- Condition A: In the Microsoft 365 Security & Compliance Center, the Exchange Online Spam Policies are not set to notify administrators when a sender in the organization has been blocked for sending spam emails.
- Condition B: Using PowerShell, the `NotifyOutboundSpam` and `NotifyOutboundSpamContact` properties are not correctly set in all relevant spam filter policies.
#>
} }
process { process {
# Get the default hosted outbound spam filter policy try {
$hostedOutboundSpamFilterPolicy = Get-HostedOutboundSpamFilterPolicy | Where-Object { $_.IsDefault -eq $true } # 2.1.6 Ensure Exchange Online Spam Policies are set to notify administrators
# Check if both settings are enabled # Retrieve the default hosted outbound spam filter policy
$bccSuspiciousOutboundMailEnabled = $hostedOutboundSpamFilterPolicy.BccSuspiciousOutboundMail $hostedOutboundSpamFilterPolicy = Get-HostedOutboundSpamFilterPolicy | Where-Object { $_.IsDefault -eq $true }
$notifyOutboundSpamEnabled = $hostedOutboundSpamFilterPolicy.NotifyOutboundSpam
$areSettingsEnabled = $bccSuspiciousOutboundMailEnabled -and $notifyOutboundSpamEnabled
# Prepare failure details if any setting is not enabled # Check if both settings are enabled (Condition A and Condition B for pass)
$failureDetails = @() $bccSuspiciousOutboundMailEnabled = $hostedOutboundSpamFilterPolicy.BccSuspiciousOutboundMail
if (-not $bccSuspiciousOutboundMailEnabled) { $notifyOutboundSpamEnabled = $hostedOutboundSpamFilterPolicy.NotifyOutboundSpam
$failureDetails += "BccSuspiciousOutboundMail is not enabled." $areSettingsEnabled = $bccSuspiciousOutboundMailEnabled -and $notifyOutboundSpamEnabled
# Prepare failure details if any setting is not enabled (Condition A and Condition B for fail)
$failureDetails = @()
if (-not $bccSuspiciousOutboundMailEnabled) {
$failureDetails += "BccSuspiciousOutboundMail is not enabled."
}
if (-not $notifyOutboundSpamEnabled) {
$failureDetails += "NotifyOutboundSpam is not enabled."
}
# Create an instance of CISAuditResult and populate it
$params = @{
Rec = $recnum
Result = $areSettingsEnabled
Status = if ($areSettingsEnabled) { "Pass" } else { "Fail" }
Details = if ($areSettingsEnabled) { "Both BccSuspiciousOutboundMail and NotifyOutboundSpam are enabled." } else { $failureDetails -join ' ' }
FailureReason = if (-not $areSettingsEnabled) { "One or both spam policies are not set to notify administrators." } else { "N/A" }
}
$auditResult = Initialize-CISAuditResult @params
} }
if (-not $notifyOutboundSpamEnabled) { catch {
$failureDetails += "NotifyOutboundSpam is not enabled." Write-Error "An error occurred during the test: $_"
# Retrieve the description from the test definitions
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
} }
# Create an instance of CISAuditResult and populate it
$auditResult = [CISAuditResult]::new()
$auditResult.Status = if ($areSettingsEnabled) { "Pass" } else { "Fail" }
$auditResult.ELevel = "E3"
$auditResult.ProfileLevel = "L1"
$auditResult.Rec = "2.1.6"
$auditResult.RecDescription = "Ensure Exchange Online Spam Policies are set to notify administrators"
$auditResult.CISControlVer = "v8"
$auditResult.CISControl = "17.5"
$auditResult.CISDescription = "Assign Key Roles and Responsibilities"
$auditResult.IG1 = $false
$auditResult.IG2 = $true
$auditResult.IG3 = $true
$auditResult.Result = $areSettingsEnabled
$auditResult.Details = if ($areSettingsEnabled) { "Both BccSuspiciousOutboundMail and NotifyOutboundSpam are enabled." } else { $failureDetails -join ' ' }
$auditResult.FailureReason = if (-not $areSettingsEnabled) { "One or both spam policies are not set to notify administrators." } else { "N/A" }
$auditResults += $auditResult
} }
end { end {
# Return auditResults # Return auditResult
return $auditResults return $auditResult
} }
} }

View File

@@ -1,52 +1,76 @@
function Test-TeamsExternalAccess { function Test-TeamsExternalAccess {
[CmdletBinding()] [CmdletBinding()]
[OutputType([CISAuditResult])]
param ( param (
# Aligned
# Parameters can be defined here if needed # Parameters can be defined here if needed
) )
begin { begin {
# Dot source the class script # Dot source the class script if necessary
# . .\source\Classes\CISAuditResult.ps1
# Initialization code, if needed
$auditResults = @() $auditResult = [CISAuditResult]::new()
$recnum = "8.2.1"
} }
process { process {
# 8.2.1 (L1) Ensure 'external access' is restricted in the Teams admin center try {
# 8.2.1 (L1) Ensure 'external access' is restricted in the Teams admin center
#
# Validate test for a pass:
# - Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
# - Specific conditions to check:
# - Condition A: The `AllowTeamsConsumer` setting is `False`.
# - Condition B: The `AllowPublicUsers` setting is `False`.
# - Condition C: The `AllowFederatedUsers` setting is `False` or, if `True`, the `AllowedDomains` contains only authorized domain names.
#
# Validate test for a fail:
# - Confirm that the failure conditions in the automated test are consistent with the manual audit results.
# - Specific conditions to check:
# - Condition A: The `AllowTeamsConsumer` setting is not `False`.
# - Condition B: The `AllowPublicUsers` setting is not `False`.
# - Condition C: The `AllowFederatedUsers` setting is `True` and the `AllowedDomains` contains unauthorized domain names or is not configured correctly.
# Connect to Teams PowerShell using Connect-MicrosoftTeams # Connect to Teams PowerShell using Connect-MicrosoftTeams
$externalAccessConfig = Get-CsTenantFederationConfiguration $externalAccessConfig = Get-CsTenantFederationConfiguration
$allowedDomainsLimited = $false $allowedDomainsLimited = $false
if ($externalAccessConfig.AllowFederatedUsers -and $externalAccessConfig.AllowedDomains -and $externalAccessConfig.AllowedDomains.AllowedDomain.Count -gt 0) { if ($externalAccessConfig.AllowFederatedUsers -and $externalAccessConfig.AllowedDomains -and $externalAccessConfig.AllowedDomains.AllowedDomain.Count -gt 0) {
$allowedDomainsLimited = $true $allowedDomainsLimited = $true
}
# Check if the configurations are as recommended
$isCompliant = -not $externalAccessConfig.AllowTeamsConsumer -and -not $externalAccessConfig.AllowPublicUsers -and (-not $externalAccessConfig.AllowFederatedUsers -or $allowedDomainsLimited)
# Create an instance of CISAuditResult and populate it
$params = @{
Rec = $recnum
Result = $isCompliant
Status = if ($isCompliant) { "Pass" } else { "Fail" }
Details = "AllowTeamsConsumer: $($externalAccessConfig.AllowTeamsConsumer); AllowPublicUsers: $($externalAccessConfig.AllowPublicUsers); AllowFederatedUsers: $($externalAccessConfig.AllowFederatedUsers); AllowedDomains limited: $allowedDomainsLimited"
FailureReason = if (-not $isCompliant) { "One or more external access configurations are not compliant." } else { "N/A" }
}
$auditResult = Initialize-CISAuditResult @params
} }
catch {
Write-Error "An error occurred during the test: $_"
# Check if the configurations are as recommended # Retrieve the description from the test definitions
$isCompliant = -not $externalAccessConfig.AllowTeamsConsumer -and -not $externalAccessConfig.AllowPublicUsers -and (-not $externalAccessConfig.AllowFederatedUsers -or $allowedDomainsLimited) $testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
# Create an instance of CISAuditResult and populate it $script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
$auditResult = [CISAuditResult]::new()
$auditResult.CISControlVer = "v8"
$auditResult.CISControl = "0.0" # The control is Explicitly Not Mapped as per the image provided
$auditResult.CISDescription = "Explicitly Not Mapped"
$auditResult.Rec = "8.2.1"
$auditResult.ELevel = "E3"
$auditResult.ProfileLevel = "L2"
$auditResult.IG1 = $false # Set based on the CIS Controls image
$auditResult.IG2 = $false # Set based on the CIS Controls image
$auditResult.IG3 = $false # Set based on the CIS Controls image
$auditResult.RecDescription = "Ensure 'external access' is restricted in the Teams admin center"
$auditResult.Result = $isCompliant
$auditResult.Details = "AllowTeamsConsumer: $($externalAccessConfig.AllowTeamsConsumer); AllowPublicUsers: $($externalAccessConfig.AllowPublicUsers); AllowFederatedUsers: $($externalAccessConfig.AllowFederatedUsers); AllowedDomains limited: $allowedDomainsLimited"
$auditResult.FailureReason = if (-not $isCompliant) { "One or more external access configurations are not compliant." } else { "N/A" }
$auditResult.Status = if ($isCompliant) { "Pass" } else { "Fail" }
$auditResults += $auditResult # Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
} }
end { end {
# Return auditResults # Return auditResult
return $auditResults return $auditResult
} }
} }

View File

@@ -1,56 +1,70 @@
function Test-TeamsExternalFileSharing { function Test-TeamsExternalFileSharing {
[CmdletBinding()] [CmdletBinding()]
[OutputType([CISAuditResult])]
param ( param (
# Aligned
# Parameters can be added here if needed # Parameters can be added here if needed
) )
begin { begin {
# Dot source the class script # Dot source the class script if necessary
# . .\source\Classes\CISAuditResult.ps1
$auditResults = @() # Initialization code, if needed
$recnum = "8.1.1"
} }
process { process {
# 8.1.1 (L2) Ensure external file sharing in Teams is enabled for only approved cloud storage services try {
# Connect to Teams PowerShell using Connect-MicrosoftTeams # 8.1.1 (L2) Ensure external file sharing in Teams is enabled for only approved cloud storage services
# Connect to Teams PowerShell using Connect-MicrosoftTeams
# Assuming that 'approvedProviders' is a list of approved cloud storage service names # Condition A: The `AllowDropbox` setting is set to `False`.
# This list must be defined according to your organization's approved cloud storage services # Condition B: The `AllowBox` setting is set to `False`.
$approvedProviders = @("AllowDropBox", "AllowBox", "AllowGoogleDrive", "AllowShareFile", "AllowEgnyte") # Condition C: The `AllowGoogleDrive` setting is set to `False`.
$clientConfig = Get-CsTeamsClientConfiguration # Condition D: The `AllowShareFile` setting is set to `False`.
# Condition E: The `AllowEgnyte` setting is set to `False`.
$isCompliant = $true # Assuming that 'approvedProviders' is a list of approved cloud storage service names
$nonCompliantProviders = @() # This list must be defined according to your organization's approved cloud storage services
$approvedProviders = @("AllowDropBox", "AllowBox", "AllowGoogleDrive", "AllowShareFile", "AllowEgnyte")
$clientConfig = Get-CsTeamsClientConfiguration
foreach ($provider in $approvedProviders) { $isCompliant = $true
if (-not $clientConfig.$provider) { $nonCompliantProviders = @()
$isCompliant = $false
$nonCompliantProviders += $provider foreach ($provider in $approvedProviders) {
if (-not $clientConfig.$provider) {
$isCompliant = $false
$nonCompliantProviders += $provider
}
} }
# Create an instance of CISAuditResult and populate it
$params = @{
Rec = $recnum
Result = $isCompliant
Status = if ($isCompliant) { "Pass" } else { "Fail" }
Details = if (-not $isCompliant) { "Non-approved providers enabled: $($nonCompliantProviders -join ', ')" } else { "All cloud storage services are approved providers" }
FailureReason = if (-not $isCompliant) { "The following non-approved providers are enabled: $($nonCompliantProviders -join ', ')" } else { "N/A" }
}
$auditResult = Initialize-CISAuditResult @params
} }
catch {
Write-Error "An error occurred during the test: $_"
# Create an instance of CISAuditResult and populate it # Retrieve the description from the test definitions
$auditResult = [CISAuditResult]::new() $testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$auditResult.CISControlVer = "v8" $description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$auditResult.CISControl = "3.3"
$auditResult.CISDescription = "Configure Data Access Control Lists"
$auditResult.Rec = "8.1.1"
$auditResult.ELevel = "E3"
$auditResult.ProfileLevel = "L2"
$auditResult.IG1 = $true # Set based on the benchmark
$auditResult.IG2 = $true # Set based on the benchmark
$auditResult.IG3 = $true # Set based on the benchmark
$auditResult.RecDescription = "Ensure external file sharing in Teams is enabled for only approved cloud storage services"
$auditResult.Result = $isCompliant
$auditResult.Details = if (-not $isCompliant) { "Non-approved providers enabled: $($nonCompliantProviders -join ', ')" } else { "All cloud storage services are approved providers" }
$auditResult.FailureReason = if (-not $isCompliant) { "The following non-approved providers are enabled: $($nonCompliantProviders -join ', ')" } else { "N/A" }
$auditResult.Status = if ($isCompliant) { "Pass" } else { "Fail" }
$auditResults += $auditResult $script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
} }
end { end {
# Return auditResults # Return auditResult
return $auditResults return $auditResult
} }
} }

212
test-gh.ps1 Normal file
View File

@@ -0,0 +1,212 @@
$repoOwner = "CriticalSolutionsNetwork"
$repoName = "M365FoundationsCISReport"
$directoryPath = ".\source\tests"
$projectName = "Test Validation Project"
# Function to create GitHub issues
function Create-GitHubIssue {
param (
[string]$title,
[string]$body,
[string]$project
)
# Create the issue and add it to the specified project
$issue = gh issue create --repo "$repoOwner/$repoName" --title "$title" --body "$body" --project "$project"
return $issue
}
# Load test definitions from CSV
$testDefinitionsPath = ".\source\helper\TestDefinitions.csv"
$testDefinitions = Import-Csv -Path $testDefinitionsPath
# Iterate over each .ps1 file in the directory
Get-ChildItem -Path $directoryPath -Filter "*.ps1" | ForEach-Object {
$fileName = $_.Name
$testDefinition = $testDefinitions | Where-Object { $_.TestFileName -eq $fileName }
if ($testDefinition) {
$rec = $testDefinition.Rec
$elevel = $testDefinition.ELevel
$profileLevel = $testDefinition.ProfileLevel
$ig1 = $testDefinition.IG1
$ig2 = $testDefinition.IG2
$ig3 = $testDefinition.IG3
$connection = $testDefinition.Connection
$issueTitle = "Rec: $rec - Validate $fileName, ELevel: $elevel, ProfileLevel: $profileLevel, IG1: $ig1, IG2: $ig2, IG3: $ig3, Connection: $connection"
$issueBody = @"
# Validation for $fileName
## Tasks
- [ ] Validate test for a pass
- Description of passing criteria:
- [ ] Validate test for a fail
- Description of failing criteria:
- [ ] Add notes and observations
- Placeholder for additional notes:
"@
# Create the issue using GitHub CLI
try {
Create-GitHubIssue -title "$issueTitle" -body "$issueBody" -project "$projectName"
Write-Output "Created issue for $fileName"
}
catch {
Write-Error "Failed to create issue for $fileName`: $_"
}
# Introduce a delay of 2 seconds
Start-Sleep -Seconds 2
}
else {
Write-Warning "No matching test definition found for $fileName"
}
}
######################################
$repoOwner = "CriticalSolutionsNetwork"
$repoName = "M365FoundationsCISReport"
# Function to update GitHub issue
function Update-GitHubTIssue {
param (
[int]$issueNumber,
[string]$title,
[string]$body,
[string]$owner,
[string]$repositoryName
)
# Update the issue using Set-GitHubIssue
Set-GitHubIssue -OwnerName $owner -RepositoryName $repositoryName -Issue $issueNumber -Title $title -Body $body -Label @("documentation", "help wanted", "question") -Confirm:$false
}
# Load test definitions from CSV
$testDefinitionsPath = ".\source\helper\TestDefinitions.csv"
$testDefinitions = Import-Csv -Path $testDefinitionsPath
# Fetch existing issues that start with "Rec:"
$existingIssues = Get-GitHubIssue -OwnerName 'CriticalSolutionsNetwork' -RepositoryName 'M365FoundationsCISReport'
# Create a list to hold matched issues
$matchedIssues = @()
$warnings = @()
# Iterate over each existing issue
$existingIssues | ForEach-Object {
$issueNumber = $_.Number
$issueTitle = $_.Title
$issueBody = $_.Body
# Extract the rec number from the issue title
if ($issueTitle -match "Rec: (\d+\.\d+\.\d+)") {
$rec = $matches[1]
# Find the matching test definition based on rec number
$testDefinition = $testDefinitions | Where-Object { $_.Rec -eq $rec }
if ($testDefinition) {
# Create the new issue body
$newIssueBody = @"
# Validation for $($testDefinition.TestFileName)
## Recommendation Details
- **Recommendation**: $($testDefinition.Rec)
- **Description**: $($testDefinition.RecDescription)
- **ELevel**: $($testDefinition.ELevel)
- **Profile Level**: $($testDefinition.ProfileLevel)
- **CIS Control**: $($testDefinition.CISControl)
- **CIS Description**: $($testDefinition.CISDescription)
- **Implementation Group 1**: $($testDefinition.IG1)
- **Implementation Group 2**: $($testDefinition.IG2)
- **Implementation Group 3**: $($testDefinition.IG3)
- **Automated**: $($testDefinition.Automated)
- **Connection**: $($testDefinition.Connection)
## [$($testDefinition.TestFileName)](https://github.com/CriticalSolutionsNetwork/M365FoundationsCISReport/blob/main/source/tests/$($testDefinition.TestFileName))
## Tasks
### Validate recommendation details
- [ ] Confirm that the recommendation details are accurate and complete as per the CIS benchmark.
### Validate test for a pass
- [ ] Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
- Specific conditions to check:
- Condition A: (Detail about what constitutes Condition A)
- Condition B: (Detail about what constitutes Condition B)
- Condition C: (Detail about what constitutes Condition C)
### Validate test for a fail
- [ ] Confirm that the failure conditions in the automated test are consistent with the manual audit results.
- Specific conditions to check:
- Condition A: (Detail about what constitutes Condition A)
- Condition B: (Detail about what constitutes Condition B)
- Condition C: (Detail about what constitutes Condition C)
### Add notes and observations
- [ ] Compare the automated audit results with the manual audit steps and provide detailed observations.
- Automated audit produced info consistent with the manual audit test results? (Yes/No)
- Without disclosing any sensitive information, document any discrepancies between the actual output and the expected output.
- Document any error messages, removing any sensitive information before submitting.
- Identify the specific function, line, or section of the script that failed, if known.
- Provide any additional context or observations that might help in troubleshooting.
If needed, the helpers folder in .\source\helpers contains a CSV to assist with locating the test definition.
"@
# Add to matched issues list
$matchedIssues += [PSCustomObject]@{
IssueNumber = $issueNumber
Title = $issueTitle
NewBody = $newIssueBody
}
} else {
$warnings += "No matching test definition found for Rec: $rec"
}
} else {
$warnings += "No matching rec number found in issue title #$issueNumber"
}
}
# Display matched issues for confirmation
if ($matchedIssues.Count -gt 0) {
Write-Output "Matched Issues:"
$matchedIssues | ForEach-Object {
Write-Output $_.Title
}
$confirmation = Read-Host "Do you want to proceed with updating these issues? (yes/no)"
if ($confirmation -eq 'yes') {
# Update the issues
$matchedIssues | ForEach-Object {
try {
Update-GitHubTIssue -issueNumber $_.IssueNumber -title $_.Title -body $_.NewBody -owner $repoOwner -repositoryName $repoName
Write-Output "Updated issue #$($_.IssueNumber)"
} catch {
Write-Error "Failed to update issue #$($_.IssueNumber): $_"
}
# Introduce a delay of 2 seconds
Start-Sleep -Seconds 2
}
} else {
Write-Output "Update canceled by user."
}
} else {
Write-Output "No matched issues found to update."
}
# Display any warnings that were captured
if ($warnings.Count -gt 0) {
Write-Output "Warnings:"
$warnings | ForEach-Object {
Write-Output $_
}
}
# Test command to verify GitHub access
Get-GitHubRepository -OwnerName 'CriticalSolutionsNetwork' -RepositoryName 'M365FoundationsCISReport'

View File

@@ -0,0 +1,27 @@
$ProjectPath = "$PSScriptRoot\..\..\.." | Convert-Path
$ProjectName = ((Get-ChildItem -Path $ProjectPath\*\*.psd1).Where{
($_.Directory.Name -match 'source|src' -or $_.Directory.Name -eq $_.BaseName) -and
$(try { Test-ModuleManifest $_.FullName -ErrorAction Stop } catch { $false } )
}).BaseName
Import-Module $ProjectName
InModuleScope $ProjectName {
Describe Get-PrivateFunction {
Context 'Default' {
BeforeEach {
$return = Get-PrivateFunction -PrivateData 'string'
}
It 'Returns a single object' {
($return | Measure-Object).Count | Should -Be 1
}
It 'Returns a string based on the parameter PrivateData' {
$return | Should -Be 'string'
}
}
}
}

View File

@@ -0,0 +1,27 @@
$ProjectPath = "$PSScriptRoot\..\..\.." | Convert-Path
$ProjectName = ((Get-ChildItem -Path $ProjectPath\*\*.psd1).Where{
($_.Directory.Name -match 'source|src' -or $_.Directory.Name -eq $_.BaseName) -and
$(try { Test-ModuleManifest $_.FullName -ErrorAction Stop } catch { $false } )
}).BaseName
Import-Module $ProjectName
InModuleScope $ProjectName {
Describe Get-PrivateFunction {
Context 'Default' {
BeforeEach {
$return = Get-PrivateFunction -PrivateData 'string'
}
It 'Returns a single object' {
($return | Measure-Object).Count | Should -Be 1
}
It 'Returns a string based on the parameter PrivateData' {
$return | Should -Be 'string'
}
}
}
}

View File

@@ -0,0 +1,27 @@
$ProjectPath = "$PSScriptRoot\..\..\.." | Convert-Path
$ProjectName = ((Get-ChildItem -Path $ProjectPath\*\*.psd1).Where{
($_.Directory.Name -match 'source|src' -or $_.Directory.Name -eq $_.BaseName) -and
$(try { Test-ModuleManifest $_.FullName -ErrorAction Stop } catch { $false } )
}).BaseName
Import-Module $ProjectName
InModuleScope $ProjectName {
Describe Get-PrivateFunction {
Context 'Default' {
BeforeEach {
$return = Get-PrivateFunction -PrivateData 'string'
}
It 'Returns a single object' {
($return | Measure-Object).Count | Should -Be 1
}
It 'Returns a string based on the parameter PrivateData' {
$return | Should -Be 'string'
}
}
}
}

View File

@@ -0,0 +1,27 @@
$ProjectPath = "$PSScriptRoot\..\..\.." | Convert-Path
$ProjectName = ((Get-ChildItem -Path $ProjectPath\*\*.psd1).Where{
($_.Directory.Name -match 'source|src' -or $_.Directory.Name -eq $_.BaseName) -and
$(try { Test-ModuleManifest $_.FullName -ErrorAction Stop } catch { $false } )
}).BaseName
Import-Module $ProjectName
InModuleScope $ProjectName {
Describe Get-PrivateFunction {
Context 'Default' {
BeforeEach {
$return = Get-PrivateFunction -PrivateData 'string'
}
It 'Returns a single object' {
($return | Measure-Object).Count | Should -Be 1
}
It 'Returns a string based on the parameter PrivateData' {
$return | Should -Be 'string'
}
}
}
}

View File

@@ -0,0 +1,27 @@
$ProjectPath = "$PSScriptRoot\..\..\.." | Convert-Path
$ProjectName = ((Get-ChildItem -Path $ProjectPath\*\*.psd1).Where{
($_.Directory.Name -match 'source|src' -or $_.Directory.Name -eq $_.BaseName) -and
$(try { Test-ModuleManifest $_.FullName -ErrorAction Stop } catch { $false } )
}).BaseName
Import-Module $ProjectName
InModuleScope $ProjectName {
Describe Get-PrivateFunction {
Context 'Default' {
BeforeEach {
$return = Get-PrivateFunction -PrivateData 'string'
}
It 'Returns a single object' {
($return | Measure-Object).Count | Should -Be 1
}
It 'Returns a string based on the parameter PrivateData' {
$return | Should -Be 'string'
}
}
}
}

View File

@@ -0,0 +1,27 @@
$ProjectPath = "$PSScriptRoot\..\..\.." | Convert-Path
$ProjectName = ((Get-ChildItem -Path $ProjectPath\*\*.psd1).Where{
($_.Directory.Name -match 'source|src' -or $_.Directory.Name -eq $_.BaseName) -and
$(try { Test-ModuleManifest $_.FullName -ErrorAction Stop } catch { $false } )
}).BaseName
Import-Module $ProjectName
InModuleScope $ProjectName {
Describe Get-PrivateFunction {
Context 'Default' {
BeforeEach {
$return = Get-PrivateFunction -PrivateData 'string'
}
It 'Returns a single object' {
($return | Measure-Object).Count | Should -Be 1
}
It 'Returns a string based on the parameter PrivateData' {
$return | Should -Be 'string'
}
}
}
}

View File

@@ -0,0 +1,27 @@
$ProjectPath = "$PSScriptRoot\..\..\.." | Convert-Path
$ProjectName = ((Get-ChildItem -Path $ProjectPath\*\*.psd1).Where{
($_.Directory.Name -match 'source|src' -or $_.Directory.Name -eq $_.BaseName) -and
$(try { Test-ModuleManifest $_.FullName -ErrorAction Stop } catch { $false } )
}).BaseName
Import-Module $ProjectName
InModuleScope $ProjectName {
Describe Get-PrivateFunction {
Context 'Default' {
BeforeEach {
$return = Get-PrivateFunction -PrivateData 'string'
}
It 'Returns a single object' {
($return | Measure-Object).Count | Should -Be 1
}
It 'Returns a string based on the parameter PrivateData' {
$return | Should -Be 'string'
}
}
}
}

View File

@@ -0,0 +1,27 @@
$ProjectPath = "$PSScriptRoot\..\..\.." | Convert-Path
$ProjectName = ((Get-ChildItem -Path $ProjectPath\*\*.psd1).Where{
($_.Directory.Name -match 'source|src' -or $_.Directory.Name -eq $_.BaseName) -and
$(try { Test-ModuleManifest $_.FullName -ErrorAction Stop } catch { $false } )
}).BaseName
Import-Module $ProjectName
InModuleScope $ProjectName {
Describe Get-PrivateFunction {
Context 'Default' {
BeforeEach {
$return = Get-PrivateFunction -PrivateData 'string'
}
It 'Returns a single object' {
($return | Measure-Object).Count | Should -Be 1
}
It 'Returns a string based on the parameter PrivateData' {
$return | Should -Be 'string'
}
}
}
}

View File

@@ -0,0 +1,27 @@
$ProjectPath = "$PSScriptRoot\..\..\.." | Convert-Path
$ProjectName = ((Get-ChildItem -Path $ProjectPath\*\*.psd1).Where{
($_.Directory.Name -match 'source|src' -or $_.Directory.Name -eq $_.BaseName) -and
$(try { Test-ModuleManifest $_.FullName -ErrorAction Stop } catch { $false } )
}).BaseName
Import-Module $ProjectName
InModuleScope $ProjectName {
Describe Get-PrivateFunction {
Context 'Default' {
BeforeEach {
$return = Get-PrivateFunction -PrivateData 'string'
}
It 'Returns a single object' {
($return | Measure-Object).Count | Should -Be 1
}
It 'Returns a string based on the parameter PrivateData' {
$return | Should -Be 'string'
}
}
}
}

Some files were not shown because too many files have changed in this diff Show More