├── .github
└── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
├── .gitignore
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── Configuration
├── DomainSettings.xml
└── ScriptSettings.xml
├── Documentation
├── Log Management.md
├── Module Creation.md
├── PSO Management.md
├── Step-by-Step - Add a DC.md
├── Step-by-Step - Deploy Hello-my-Dir.md
├── User Manual for release 1.0.0.md
├── User Manual for release 1.1.0.md
└── what's new in 1.1.2 QF 02.md
├── Imports
├── Default Domain Controllers Security
│ └── {9947AFBC-5433-47E0-AFB8-E7CBD1D23887}
│ │ ├── Backup.xml
│ │ ├── DomainSysvol
│ │ └── GPO
│ │ │ └── Machine
│ │ │ ├── comment.cmtx
│ │ │ ├── microsoft
│ │ │ └── windows nt
│ │ │ │ ├── Audit
│ │ │ │ └── audit.csv
│ │ │ │ └── SecEdit
│ │ │ │ └── GptTmpl.inf
│ │ │ └── registry.pol
│ │ ├── bkupInfo.xml
│ │ └── gpreport.xml
├── Default Domain Security
│ └── {0B0FC9E4-A7D9-4800-9AF9-272E38C6CCCE}
│ │ ├── Backup.xml
│ │ ├── DomainSysvol
│ │ └── GPO
│ │ │ └── Machine
│ │ │ ├── Preferences
│ │ │ └── Registry
│ │ │ │ └── Registry.xml
│ │ │ ├── comment.cmtx
│ │ │ ├── microsoft
│ │ │ └── windows nt
│ │ │ │ └── SecEdit
│ │ │ │ └── GptTmpl.inf
│ │ │ └── registry.pol
│ │ ├── bkupInfo.xml
│ │ └── gpreport.xml
└── MS LAPS
│ └── LAPS.x64.msi
├── Invoke-HelloMyDIR.ps1
├── LICENSE
├── Logs
└── .touch
├── Modules
├── Module-Hardening
│ ├── Module-Hardening.psd1
│ └── Module-Hardening.psm1
├── Module-HmD
│ ├── Module-HmD.psd1
│ └── Module-HmD.psm1
├── Module-Logs
│ ├── Module-Logs.psd1
│ └── Module-Logs.psm1
├── Module-MSfix
│ ├── Module-MSfix.psd1
│ └── Module-MSfix.psm1
├── Module-PC_3-2-0-1
│ ├── Module-PC_3-2-0-1.psd1
│ └── Module-PC_3-2-0-1.psm1
├── Module-PK_4-2
│ ├── Module-PK_4-2.psd1
│ └── Module-PK_4-2.psm1
├── Module-Passwords
│ ├── Module-Passwords.psd1
│ └── Module-Passwords.psm1
├── Module-Screening
│ ├── Module-Screening.psd1
│ ├── Module-Screening.psm1
│ └── Module-Screening.xml
└── Module-Xml
│ ├── Module-Xml.psd1
│ └── Module-Xml.psm1
├── README.md
├── SECURITY.md
└── prepare-test.ps1
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 | 1. Go to '...'
16 | 2. Click on '....'
17 | 3. Scroll down to '....'
18 | 4. See error
19 |
20 | **Expected behavior**
21 | A clear and concise description of what you expected to happen.
22 |
23 | **Screenshots**
24 | If applicable, add screenshots to help explain your problem.
25 |
26 | **Desktop (please complete the following information):**
27 | - OS: [e.g. iOS]
28 | - Browser [e.g. chrome, safari]
29 | - Version [e.g. 22]
30 |
31 | **Smartphone (please complete the following information):**
32 | - Device: [e.g. iPhone6]
33 | - OS: [e.g. iOS8.1]
34 | - Browser [e.g. stock browser, safari]
35 | - Version [e.g. 22]
36 |
37 | **Additional context**
38 | Add any other context about the problem here.
39 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore MAC OS specific files
2 | # General
3 | .DS_Store
4 | .AppleDouble
5 | .LSOverride
6 |
7 | # Specific to this code
8 | .dbg
9 |
10 | # Icon must end with two \r
11 | Icon
12 |
13 | # Thumbnails
14 | ._*
15 |
16 | # Files that might appear in the root of a volume
17 | .DocumentRevisions-V100
18 | .fseventsd
19 | .Spotlight-V100
20 | .TemporaryItems
21 | .Trashes
22 | .VolumeIcon.icns
23 | .com.apple.timemachine.donotpresent
24 |
25 | # Directories potentially created on remote AFP share
26 | .AppleDB
27 | .AppleDesktop
28 | Network Trash Folder
29 | Temporary Items
30 | .apdisk
31 |
32 | ## Ignore Visual Studio temporary files, build results, and
33 | ## files generated by popular Visual Studio add-ons.
34 | ##
35 | ## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore
36 |
37 | # User-specific files
38 | *.rsuser
39 | *.suo
40 | *.user
41 | *.userosscache
42 | *.sln.docstates
43 |
44 | # User-specific files (MonoDevelop/Xamarin Studio)
45 | *.userprefs
46 |
47 | # Mono auto generated files
48 | mono_crash.*
49 |
50 | # Build results
51 | [Dd]ebug/
52 | [Dd]ebugPublic/
53 | [Rr]elease/
54 | [Rr]eleases/
55 | x64/
56 | x86/
57 | [Ww][Ii][Nn]32/
58 | [Aa][Rr][Mm]/
59 | [Aa][Rr][Mm]64/
60 | bld/
61 | [Bb]in/
62 | [Oo]bj/
63 | #[Ll]og/
64 | #[Ll]ogs/
65 |
66 | # Visual Studio 2015/2017 cache/options directory
67 | .vs/
68 | # Uncomment if you have tasks that create the project's static files in wwwroot
69 | #wwwroot/
70 |
71 | # Visual Studio 2017 auto generated files
72 | Generated\ Files/
73 |
74 | # MSTest test Results
75 | [Tt]est[Rr]esult*/
76 | [Bb]uild[Ll]og.*
77 |
78 | # NUnit
79 | *.VisualState.xml
80 | TestResult.xml
81 | nunit-*.xml
82 |
83 | # Build Results of an ATL Project
84 | [Dd]ebugPS/
85 | [Rr]eleasePS/
86 | dlldata.c
87 |
88 | # Benchmark Results
89 | BenchmarkDotNet.Artifacts/
90 |
91 | # .NET Core
92 | project.lock.json
93 | project.fragment.lock.json
94 | artifacts/
95 |
96 | # ASP.NET Scaffolding
97 | ScaffoldingReadMe.txt
98 |
99 | # StyleCop
100 | StyleCopReport.xml
101 |
102 | # Files built by Visual Studio
103 | *_i.c
104 | *_p.c
105 | *_h.h
106 | *.ilk
107 | *.meta
108 | *.obj
109 | *.iobj
110 | *.pch
111 | *.pdb
112 | *.ipdb
113 | *.pgc
114 | *.pgd
115 | *.rsp
116 | *.sbr
117 | *.tlb
118 | *.tli
119 | *.tlh
120 | *.tmp
121 | *.tmp_proj
122 | *_wpftmp.csproj
123 | *.log
124 | *.tlog
125 | *.vspscc
126 | *.vssscc
127 | .builds
128 | *.pidb
129 | *.svclog
130 | *.scc
131 |
132 | # Chutzpah Test files
133 | _Chutzpah*
134 |
135 | # Visual C++ cache files
136 | ipch/
137 | *.aps
138 | *.ncb
139 | *.opendb
140 | *.opensdf
141 | *.sdf
142 | *.cachefile
143 | *.VC.db
144 | *.VC.VC.opendb
145 |
146 | # Visual Studio profiler
147 | *.psess
148 | *.vsp
149 | *.vspx
150 | *.sap
151 |
152 | # Visual Studio Trace Files
153 | *.e2e
154 |
155 | # TFS 2012 Local Workspace
156 | $tf/
157 |
158 | # Guidance Automation Toolkit
159 | *.gpState
160 |
161 | # ReSharper is a .NET coding add-in
162 | _ReSharper*/
163 | *.[Rr]e[Ss]harper
164 | *.DotSettings.user
165 |
166 | # TeamCity is a build add-in
167 | _TeamCity*
168 |
169 | # DotCover is a Code Coverage Tool
170 | *.dotCover
171 |
172 | # AxoCover is a Code Coverage Tool
173 | .axoCover/*
174 | !.axoCover/settings.json
175 |
176 | # Coverlet is a free, cross platform Code Coverage Tool
177 | coverage*.json
178 | coverage*.xml
179 | coverage*.info
180 |
181 | # Visual Studio code coverage results
182 | *.coverage
183 | *.coveragexml
184 |
185 | # NCrunch
186 | _NCrunch_*
187 | .*crunch*.local.xml
188 | nCrunchTemp_*
189 |
190 | # MightyMoose
191 | *.mm.*
192 | AutoTest.Net/
193 |
194 | # Web workbench (sass)
195 | .sass-cache/
196 |
197 | # Installshield output folder
198 | [Ee]xpress/
199 |
200 | # DocProject is a documentation generator add-in
201 | DocProject/buildhelp/
202 | DocProject/Help/*.HxT
203 | DocProject/Help/*.HxC
204 | DocProject/Help/*.hhc
205 | DocProject/Help/*.hhk
206 | DocProject/Help/*.hhp
207 | DocProject/Help/Html2
208 | DocProject/Help/html
209 |
210 | # Click-Once directory
211 | publish/
212 |
213 | # Publish Web Output
214 | *.[Pp]ublish.xml
215 | *.azurePubxml
216 | # Note: Comment the next line if you want to checkin your web deploy settings,
217 | # but database connection strings (with potential passwords) will be unencrypted
218 | *.pubxml
219 | *.publishproj
220 |
221 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
222 | # checkin your Azure Web App publish settings, but sensitive information contained
223 | # in these scripts will be unencrypted
224 | PublishScripts/
225 |
226 | # NuGet Packages
227 | *.nupkg
228 | # NuGet Symbol Packages
229 | *.snupkg
230 | # The packages folder can be ignored because of Package Restore
231 | **/[Pp]ackages/*
232 | # except build/, which is used as an MSBuild target.
233 | !**/[Pp]ackages/build/
234 | # Uncomment if necessary however generally it will be regenerated when needed
235 | #!**/[Pp]ackages/repositories.config
236 | # NuGet v3's project.json files produces more ignorable files
237 | *.nuget.props
238 | *.nuget.targets
239 |
240 | # Microsoft Azure Build Output
241 | csx/
242 | *.build.csdef
243 |
244 | # Microsoft Azure Emulator
245 | ecf/
246 | rcf/
247 |
248 | # Windows Store app package directories and files
249 | AppPackages/
250 | BundleArtifacts/
251 | Package.StoreAssociation.xml
252 | _pkginfo.txt
253 | *.appx
254 | *.appxbundle
255 | *.appxupload
256 |
257 | # Visual Studio cache files
258 | # files ending in .cache can be ignored
259 | *.[Cc]ache
260 | # but keep track of directories ending in .cache
261 | !?*.[Cc]ache/
262 |
263 | # Others
264 | ClientBin/
265 | ~$*
266 | *~
267 | *.dbmdl
268 | *.dbproj.schemaview
269 | *.jfm
270 | *.pfx
271 | *.publishsettings
272 | orleans.codegen.cs
273 |
274 | # Including strong name files can present a security risk
275 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
276 | #*.snk
277 |
278 | # Since there are multiple workflows, uncomment next line to ignore bower_components
279 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
280 | #bower_components/
281 |
282 | # RIA/Silverlight projects
283 | Generated_Code/
284 |
285 | # Backup & report files from converting an old project file
286 | # to a newer Visual Studio version. Backup files are not needed,
287 | # because we have git ;-)
288 | _UpgradeReport_Files/
289 | Backup*/
290 | UpgradeLog*.XML
291 | UpgradeLog*.htm
292 | ServiceFabricBackup/
293 | *.rptproj.bak
294 |
295 | # SQL Server files
296 | *.mdf
297 | *.ldf
298 | *.ndf
299 |
300 | # Business Intelligence projects
301 | *.rdl.data
302 | *.bim.layout
303 | *.bim_*.settings
304 | *.rptproj.rsuser
305 | *- [Bb]ackup.rdl
306 | *- [Bb]ackup ([0-9]).rdl
307 | *- [Bb]ackup ([0-9][0-9]).rdl
308 |
309 | # Microsoft Fakes
310 | FakesAssemblies/
311 |
312 | # GhostDoc plugin setting file
313 | *.GhostDoc.xml
314 |
315 | # Node.js Tools for Visual Studio
316 | .ntvs_analysis.dat
317 | node_modules/
318 |
319 | # Visual Studio 6 build log
320 | *.plg
321 |
322 | # Visual Studio 6 workspace options file
323 | *.opt
324 |
325 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
326 | *.vbw
327 |
328 | # Visual Studio 6 auto-generated project file (contains which files were open etc.)
329 | *.vbp
330 |
331 | # Visual Studio 6 workspace and project file (working project files containing files to include in project)
332 | *.dsw
333 | *.dsp
334 |
335 | # Visual Studio 6 technical files
336 | *.ncb
337 | *.aps
338 |
339 | # Visual Studio LightSwitch build output
340 | **/*.HTMLClient/GeneratedArtifacts
341 | **/*.DesktopClient/GeneratedArtifacts
342 | **/*.DesktopClient/ModelManifest.xml
343 | **/*.Server/GeneratedArtifacts
344 | **/*.Server/ModelManifest.xml
345 | _Pvt_Extensions
346 |
347 | # Paket dependency manager
348 | .paket/paket.exe
349 | paket-files/
350 |
351 | # FAKE - F# Make
352 | .fake/
353 |
354 | # CodeRush personal settings
355 | .cr/personal
356 |
357 | # Python Tools for Visual Studio (PTVS)
358 | __pycache__/
359 | *.pyc
360 |
361 | # Cake - Uncomment if you are using it
362 | # tools/**
363 | # !tools/packages.config
364 |
365 | # Tabs Studio
366 | *.tss
367 |
368 | # Telerik's JustMock configuration file
369 | *.jmconfig
370 |
371 | # BizTalk build output
372 | *.btp.cs
373 | *.btm.cs
374 | *.odx.cs
375 | *.xsd.cs
376 |
377 | # OpenCover UI analysis results
378 | OpenCover/
379 |
380 | # Azure Stream Analytics local run output
381 | ASALocalRun/
382 |
383 | # MSBuild Binary and Structured Log
384 | *.binlog
385 |
386 | # NVidia Nsight GPU debugger configuration file
387 | *.nvuser
388 |
389 | # MFractors (Xamarin productivity tool) working folder
390 | .mfractor/
391 |
392 | # Local History for Visual Studio
393 | .localhistory/
394 |
395 | # Visual Studio History (VSHistory) files
396 | .vshistory/
397 |
398 | # BeatPulse healthcheck temp database
399 | healthchecksdb
400 |
401 | # Backup folder for Package Reference Convert tool in Visual Studio 2017
402 | MigrationBackup/
403 |
404 | # Ionide (cross platform F# VS Code tools) working folder
405 | .ionide/
406 |
407 | # Fody - auto-generated XML schema
408 | FodyWeavers.xsd
409 |
410 | # VS Code files for those working on multiple tools
411 | .vscode/*
412 | !.vscode/settings.json
413 | !.vscode/tasks.json
414 | !.vscode/launch.json
415 | !.vscode/extensions.json
416 | *.code-workspace
417 |
418 | # Local History for Visual Studio Code
419 | .history/
420 |
421 | # Windows Installer files from build outputs
422 | *.cab
423 | #*.msi
424 | *.msix
425 | *.msm
426 | *.msp
427 |
428 | # JetBrains Rider
429 | *.sln.iml
430 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | We as members, contributors, and leaders pledge to make participation in our
6 | community a harassment-free experience for everyone, regardless of age, body
7 | size, visible or invisible disability, ethnicity, sex characteristics, gender
8 | identity and expression, level of experience, education, socio-economic status,
9 | nationality, personal appearance, race, religion, or sexual identity
10 | and orientation.
11 |
12 | We pledge to act and interact in ways that contribute to an open, welcoming,
13 | diverse, inclusive, and healthy community.
14 |
15 | ## Our Standards
16 |
17 | Examples of behavior that contributes to a positive environment for our
18 | community include:
19 |
20 | * Demonstrating empathy and kindness toward other people
21 | * Being respectful of differing opinions, viewpoints, and experiences
22 | * Giving and gracefully accepting constructive feedback
23 | * Accepting responsibility and apologizing to those affected by our mistakes,
24 | and learning from the experience
25 | * Focusing on what is best not just for us as individuals, but for the
26 | overall community
27 |
28 | Examples of unacceptable behavior include:
29 |
30 | * The use of sexualized language or imagery, and sexual attention or
31 | advances of any kind
32 | * Trolling, insulting or derogatory comments, and personal or political attacks
33 | * Public or private harassment
34 | * Publishing others' private information, such as a physical or email
35 | address, without their explicit permission
36 | * Other conduct which could reasonably be considered inappropriate in a
37 | professional setting
38 |
39 | ## Enforcement Responsibilities
40 |
41 | Community leaders are responsible for clarifying and enforcing our standards of
42 | acceptable behavior and will take appropriate and fair corrective action in
43 | response to any behavior that they deem inappropriate, threatening, offensive,
44 | or harmful.
45 |
46 | Community leaders have the right and responsibility to remove, edit, or reject
47 | comments, commits, code, wiki edits, issues, and other contributions that are
48 | not aligned to this Code of Conduct, and will communicate reasons for moderation
49 | decisions when appropriate.
50 |
51 | ## Scope
52 |
53 | This Code of Conduct applies within all community spaces, and also applies when
54 | an individual is officially representing the community in public spaces.
55 | Examples of representing our community include using an official e-mail address,
56 | posting via an official social media account, or acting as an appointed
57 | representative at an online or offline event.
58 |
59 | ## Enforcement
60 |
61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
62 | reported to the community leaders responsible for enforcement at
63 | .
64 | All complaints will be reviewed and investigated promptly and fairly.
65 |
66 | All community leaders are obligated to respect the privacy and security of the
67 | reporter of any incident.
68 |
69 | ## Enforcement Guidelines
70 |
71 | Community leaders will follow these Community Impact Guidelines in determining
72 | the consequences for any action they deem in violation of this Code of Conduct:
73 |
74 | ### 1. Correction
75 |
76 | **Community Impact**: Use of inappropriate language or other behavior deemed
77 | unprofessional or unwelcome in the community.
78 |
79 | **Consequence**: A private, written warning from community leaders, providing
80 | clarity around the nature of the violation and an explanation of why the
81 | behavior was inappropriate. A public apology may be requested.
82 |
83 | ### 2. Warning
84 |
85 | **Community Impact**: A violation through a single incident or series
86 | of actions.
87 |
88 | **Consequence**: A warning with consequences for continued behavior. No
89 | interaction with the people involved, including unsolicited interaction with
90 | those enforcing the Code of Conduct, for a specified period of time. This
91 | includes avoiding interactions in community spaces as well as external channels
92 | like social media. Violating these terms may lead to a temporary or
93 | permanent ban.
94 |
95 | ### 3. Temporary Ban
96 |
97 | **Community Impact**: A serious violation of community standards, including
98 | sustained inappropriate behavior.
99 |
100 | **Consequence**: A temporary ban from any sort of interaction or public
101 | communication with the community for a specified period of time. No public or
102 | private interaction with the people involved, including unsolicited interaction
103 | with those enforcing the Code of Conduct, is allowed during this period.
104 | Violating these terms may lead to a permanent ban.
105 |
106 | ### 4. Permanent Ban
107 |
108 | **Community Impact**: Demonstrating a pattern of violation of community
109 | standards, including sustained inappropriate behavior, harassment of an
110 | individual, or aggression toward or disparagement of classes of individuals.
111 |
112 | **Consequence**: A permanent ban from any sort of public interaction within
113 | the community.
114 |
115 | ## Attribution
116 |
117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage],
118 | version 2.0, available at
119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
120 |
121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct
122 | enforcement ladder](https://github.com/mozilla/diversity).
123 |
124 | [homepage]: https://www.contributor-covenant.org
125 |
126 | For answers to common questions about this code of conduct, see the FAQ at
127 | https://www.contributor-covenant.org/faq. Translations are available at
128 | https://www.contributor-covenant.org/translations.
129 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | If you want to contribute to this project, please contact me.
2 |
--------------------------------------------------------------------------------
/Configuration/DomainSettings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | SID-500
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | SID-500
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/Configuration/ScriptSettings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | `{===============`
26 | `[/!\ WARNING /!\`
27 | `{===============`
28 | Once the script will have run the ADDS installation, a `|reboot` will be performed.
29 | To harden the directory, you will have to `|rerun this script`.
30 |
31 |
32 | The script will now have to perform a `|reboot` to terminate the setup.
33 |
34 |
35 |
36 |
37 |
38 |
39 | No;Nop;Han Han...;Try Again;No way;Are you kidding me?;Grmpf...;Not a chance;I don't think so;That's negative...;Sorry, but... No;I'll pass;Forget it;Not happening;I have better things to do;I have to feed my pet rock;I already have a subscription to the 'no' magazine;I don't have energy for that right now;I'm not really into that sort of thing;Maybe some other time?
40 | Let’s consider a different angle on this;I’ve encountered some contrasting insights;Might there be another side to this story?;This calls for a deeper dive into the facts;Perhaps there’s more to it than meets the eye;Are we in a parallel universe where wrong is right?;Can’t say I agree with your logic there;Don’t be fooled by your neurons
41 | Uh!;Bad;Feeling;Plan;Grrr;Coach;Play;Give;Chance;Handle:The;Rock;Dog;Crap;Hate;Spider;Down;42;Handball;Squash;Pumpkin;Bat;Skull;Black;Adams;Family
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
--------------------------------------------------------------------------------
/Documentation/Log Management.md:
--------------------------------------------------------------------------------
1 | # LOG MANAGEMENT
2 | ### How to manage loging with your scripts
3 | Logs are part of any code. This papersheet explains how logs should be used in your own repository.
4 |
5 | ## Do...
6 | * You *do* store your logs in the Event viewer.
7 | * You *do* store your logs in your own log file (.evt or .evtx).
8 | * You *do* use your unique log sources that should **never** be used in another log.
9 |
10 | ## How you can achieve the three Do rules?
11 | This script uses fonctions from *module-logs.psm1*:
12 | > **Write-toEventLog**: this function write your log text in your log file.
13 | >
14 | > **Test-EventLog**: this function ensure that your log file and log source exists.
15 |
16 | The functions *Write-DebugLog* and *Export-DebugLog* are only use to maintain a loging for the two other functions.
17 |
18 | ## How the functions works
19 | The function to write and test event log works the same way: the event log file name will always be read from the *ScriptSettings.xml* file (always present in the *.\Configuration* folder). This file contains a specific section:
20 | ```
21 |
22 |
23 |
24 |
25 | ```
26 | The logging balise contains two important value used by the function:
27 | 1. *Name*: this is the name of your log file. It should be unique, but if it already exists, then it will use it.
28 | 2. *Prefix*: This is a unique value that will be added to your script / function name to generate a unique Source ID.
29 |
30 | When you use the log function, those ones will always use the PStack Caller name as source name (so you don't have to worry about your source name. You also will easily filter your event log to troubleshot a function). Example:
31 | ```
32 | 1. The script "myScript.ps1" add log [ Write to Logname: HelloMyDir - Source: HmD_myScript ]
33 | 2. The script call a function "myFunction" [ Write to Logname: HelloMyDir - Source: HmD_MyFunction ]
34 | 3. The function now run another function "otherFunc" [ Write to Logname: HelloMyDir - Source: HmD_otherFunc ]
35 | ```
36 |
37 | ## How to use the log functions
38 | There is a two-step process to include in each of your script / function:
39 | 1. First, you should call the *Test-EventLog* to ensure the log file and the source exists.
40 | 2. Second, you use the function *Write-ToEventLog* to add your event to the log file.
41 |
42 | *Test-EventLog* doesn't needs any parameter: the logname is read from the xml configuration file and the log source is a mix of the xml configuration file (prefix) and the script or function using it (psStack caller). The function returns $True if everything went fine (logfile and source are ready to be used), or $False if an error is met. Don't forget to handle this error in your code.
43 |
44 | *Write-ToEventLog* needs two parameters:
45 | 1. *Event Type*: should be INFO, WARNING or ERROR, depending on type of message you want to log.
46 | 2. *Event Message*: This is a text array, so you can prepare all your log trace and send it once you'll get all that things up.
47 |
48 | If any issues occurs, an exit code is sent, but you don't need to handle it. Use the parameter **-ErrorAction SilentlyContinue** to manage your code with no error.
49 |
50 | Here is a code example:
51 | ```
52 | Function Run-Away {
53 | param()
54 | # initialize log (logname is '%name%' and source '%prefix%Run-Away')
55 | $initLog = Test-EventLog
56 |
57 | if (-not ($initlog)) {
58 | write-warning "The log could not be stored in event viewer! Please review your xml settings."
59 | }
60 |
61 | # Preparing log text...
62 | $myTextLog = @("This is a log demo")
63 | $myTextLog += "What a wonderfull log, isn't it?")
64 | $myTextLog += "I deserve a medal for this..."
65 |
66 | # Adding log to event log pit
67 | Write-ToEventLog INFO $myTextLog -ErrorAction SilentlyContinue
68 | }
69 | ```
70 | ## Support
71 | Contact me through GitHub for any support.
--------------------------------------------------------------------------------
/Documentation/Module Creation.md:
--------------------------------------------------------------------------------
1 | # HOW TO CREATE A NEW MODULE
2 | ## Document goal
3 | The is document will instruct on how you should proceed with module creation.
4 | Modules are managed as microsoft intend it.
5 |
6 | ## Module structure
7 | A module is composed of three distincts elements:
8 | 1. A folder
9 | 2. A module file
10 | 3. One or more script/module file.
11 |
12 | The folder is at the top of the hierarchy and must be placed in .\Modules:
13 | ```
14 | |
15 | +-+-> MODULES
16 | +-+-> MyModule
17 | +---> myModule.psd1
18 | +---> myModule.psm1
19 | ```
20 | When the script starts, it will automatically load every manifest file (.psd1) to import related module file (.psm1) or script (.ps1).
21 |
22 | ## Create your module
23 | ### Step 1: Choosing a name
24 | All modules should reflect the nature of the functions they will offers. The name should always be prefixed by 'Module-'.
25 | Example:
26 | > You will create a module that will deal with User accounts.
27 | > You will name your module: Module-UserManagement
28 |
29 | ### Step 2: Create folder
30 | Once youe module name ha been fixed, proceed with the folder creation within the .\Modules directory.
31 | The folder must be named as your module (i.e. *Module-UserManagement* as per previous example).
32 |
33 | ### Step 3: Create a manifest file
34 | Fireup PowerShell and move to your new folder. Then, run the following command to generate your manifest (remplace *module-userManagement* by your script name):
35 | ```
36 | New-ModuleManifest -Path ./Module-UserManagement.psd1 `
37 | -ModuleVersion '1.0.0.0' `
38 | -Author $env:username `
39 | -RootModule Module-UserManagement.psm1 `
40 | -NestedModules Module-UserManagement.psm1 `
41 | -Guid (New-Guid).Guid
42 | ```
43 | *RootModule* and *NestedModule* are requested for the manifest to load properly the functions within your module file.
44 |
45 | ### Step 4: Create your module file
46 | To let the module be able to load your functions, create a new file name as your folder with the extension '.psm1' (*module-userManagement.psm1* in our example), then add your function to it.
47 |
48 | ## Tips
49 | ### Test the validity of a manifest file
50 | Just run the below command against your .psd1 file to test it (adapt following your need):
51 | ```
52 | test-moduleManifest .\Modules\MyModule\myModule.psd1
53 | ```
54 | ### Load your module file
55 | To load your module and test if it works, run the below commands:
56 | ```
57 | import-module .\Modules\MyModule -verbose
58 | Get-Command -Module MyModule
59 | Get-Module
60 | ```
61 | You should get on screen the list of functions within your module and the module manifest version number (1.0.0.0 by default).
--------------------------------------------------------------------------------
/Documentation/PSO Management.md:
--------------------------------------------------------------------------------
1 | # PSO MANAGEMENT WITHIN YOUR DOMAIN
2 |
3 | ## Document goal
4 |
5 | This document is intended to assist you in properly use each PSO accordingly to your use-case.
6 | PSO are objects defining a password policy that can be ordered with priority (less is the weight, higher the priority is).
7 |
8 | ## PSO List
9 |
10 | The script automatically add the following Password Strategy Objects (PSO):
11 |
12 | ```
13 | > PSO-EmergencyAccounts-LongLive......: 5 years, complex, 30 characters, Weight is 105.
14 | > PSO-ServiceAccounts-Legacy..........: 5 years, complex, 30 characters, Weight is 105.
15 | > PSO-EmergencyAccounts-Standard......: 1 year, complex, 30 characters, Weight is 100.
16 | > PSO-Users-ChangeEvery3years.........: 3 year, complex, 16 characters, Weight is 70.
17 | > PSO-Users-ChangeEvery1year..........: 1 year, complex, 12 characters, Weight is 60.
18 | > PSO-Users-ChangeEvery3months........: 3 months, complex, 10 characters, Weight is 50.
19 | > PSO-ServiceAccounts-ExtendedLife....: 3 years, complex, 18 characters, Weight is 35.
20 | > PSO-ServiceAccounts-Standard........: 1 year, complex, 16 characters, Weight is 30.
21 | > PSO-AdminAccounts-SystemPriveleged..: 6 months, complex, 14 characters, Weight is 20.
22 | > PSO-AdminAccounts-ADdelegatedRight..: 6 months, complex, 16 characters, Weight is 15.
23 | > PSO-ServiceAccounts-ADdelegatedRight: 1 year, complex, 24 characters, Weight is 15.
24 | > PSO-AdminAccounts-ADhighPrivileges..: 6 months, complex, 20 characters, Weight is 10.
25 | ```
26 |
27 | Each PSO is linked to its corresponding Global Security Group, which is named the same (you can rename them later if you want).
28 | Because we are installing a brand new domain where such groups have to be protected, the item will be added in the following path:
29 |
30 | > PSO will be added to their default Container (CN=Password Settings Container,CN=System,DC=...)
31 | > Groups will be added to the default Users Container (CN=Users,DC=...)
32 |
33 | Groups can be relocated anywhere you want to.
34 |
35 | ## How applying a PSO
36 |
37 | To apply a PSO, simply add your User or Group object to the proper PSO group.
38 |
39 | Example:
40 |
41 | > To add Amy SiouSoMuch to the PSO "Users change their password every year",
42 | > add the Amy account to the group 'PSO-Users-ChangeEvery1year'.
43 |
44 | This method allow to fine tune your password strategy. If an object belongs to more than one PSO, the one with the lower weight win.
45 |
46 | ## What about the builtin administrator account.
47 |
48 | As this is the only account usable after a domain installation, this one will be added to the groups *PSO-EmergencyAccounts-Standard* and *PSO-AdminAccounts-ADhighPrivileges*.
49 | This will ensure that while the account is being heavily used, the password strategy linked to it will be the most restrictive one.
50 | *Once you have added your own administration accounts, simply remove the account from the group PSO-AdminAccounts-ADhighPrivileges.*
51 |
52 | ## PSO Description and usage
53 |
54 | ### PSO-EmergencyAccounts-LongLive
55 |
56 | Used to manage password strategy against Emergency Accounts when no pwd rotation are expected.
57 | An emergency account is one type of user object that should *never* be used on the domain, except when no other administration accounts works (such as the administrator accounts from your domain).
58 | Such accounts should be monitored and their password stored in a safe place, outside your network (best place is in a vault), with a restricted access.
59 |
60 | ### PSO-ServiceAccounts-Legacy
61 |
62 | Used to handle legacy service accounts that could not be changed easily.
63 | Such accounts belongs to legacy or complex applications and thier password change is either not documented or feasable.
64 | As the usual "time-to-live" for such applications is between 3 to 5 years, this PSO ensure that a legacy account will expire after 5 years if its paswors is not rotated during an upgrade.
65 | Such accounts should be well documented and identified.
66 |
67 | ### PSO-EmergencyAccounts-Standard
68 |
69 | Used to manage password strategy against Emergency Accounts when regular pwd rotation is expected. This should be your default choice for emergency accounts.
70 |
71 | ### PSO-Users-ChangeEvery3years
72 |
73 | This strategy is dedicated to users with access to *uncritical* assets or synchronized with EntraID and using strong authentication factor on the end user system. In other term: the user never type in its password...
74 |
75 | ### PSO-USers-ChangeEvery1year
76 |
77 | This strategy is dedicated to users with access to *uncritical* assets and not synched with EntraID.They may used WhfB to authenticated on their system, however they regularly use the password to authenticate.
78 |
79 | ### PSO-Users-ChangeEvery3months
80 |
81 | This strategy is dedicated to AD only users with access to **critical** assets.
82 | This strategy should be coupled to a password change auditor tool to avoid carousel...
83 |
84 | ### PSO-ServiceAccounts-ExtendedLife
85 |
86 | Used to handle service accounts used by application or appliance that are considered as safe.
87 | Such account do not need to be cycle oftenly and are only kept in line with the actual password strategy from recomandation. This is oftenly the case for account use to bind to AD with no more rights than a user has.
88 |
89 | ### PSO-ServiceAccounts-Standard
90 |
91 | Used to handle service accounts used by application or appliance.
92 | When an application/appliance needs to work with Active Directory, an account is associated to one or more of their services. Because the appliance or system linked to this service account could be theft, a regular password cycling is needed.
93 | *This should be your default choice regarding service account.*
94 |
95 | ### PSO-AdminAccounts-SystemPrivileged
96 |
97 | This strategy is dedicated to accounts used to maintain system (i.e. accounts that are local admins or close to).
98 | You should use three kinds of administration accounts (as system empowered user, as ad delegated admin user or as ad full empowered user):
99 | > *Empowered User* are dedicated to maintain Windows Systems.
100 | > *AD Delegated User* are kind of account that have specific authorization upon some parts of AD.
101 | > *AD Full Empowered User* are unlimited account having full permission upon AD.
102 |
103 | ### PSO-AdminAccounts-ADdelegatedRight
104 |
105 | This strategy is dedicated to accounts used to manage some specific AD object present in dedicated OU (i.e. accounts that inherit authorization from an AD delegation).
106 | You should use three kinds of administration accounts (as system empowered user, as ad delegated admin user or as ad full empowered user):
107 | > *Empowered User* are dedicated to maintain Windows Systems.
108 | > *AD Delegated User* are kind of account that have specific authorization upon some parts of AD.
109 | > *AD Full Empowered User* are unlimited account having full permission upon AD.
110 |
111 | ### PSO-AdminAccounts-ADhighPrivileges
112 |
113 | This strategy is dedicated to accounts used to maintain active directory (i.e. accounts that are domain admins or close to).
114 | You should use three kinds of administration accounts (as system empowered user, as ad delegated admin user or as ad full empowered user):
115 | > *Empowered User* are dedicated to maintain Windows Systems.
116 | > *AD Delegated User* are kind of account that have specific authorization upon some parts of AD.
117 | > *AD Full Empowered User* are unlimited account having full permission upon AD.
118 |
--------------------------------------------------------------------------------
/Documentation/Step-by-Step - Add a DC.md:
--------------------------------------------------------------------------------
1 | # Step-By-Step: adding a DC with Hello-my-Dir
2 | This procedure will guide you through the process of adding a second DC to your domain.
3 |
4 | ## Step 1: prepare the hole grass
5 | + Build your new windows server and assign it a static IP. the DNS client should be set to your running domain controller.
6 | + Update your server to the latest patch from Microsoft - so to say: be up-to-date.
7 | + Create a folder to your receipt the HmD files (**c:\HmD**)
8 |
9 | ## Step 2: send your seed to the hole
10 | + Login to your existing DC (on which the Hello-my-Dir binaries is...).
11 | + Fire-Up PowerShell (elevated or not).
12 | + Copy the content of your existing **c:\HmD** to your new server (same path, but this not mandatory). The following method let you proceed to a network copy by creating a temporary network share accessible only to _DLGUSER01_ in reading mode:
13 | `New-SmbShare -Temporary -ReadAccess HELLO\DLGUSER01 -Path C:\HmD -Name HmD`
14 | + Log back to your futur DC and fire-up powershell as administrator.
15 | + Create a mount point on your system (replace the domain name with yours):
16 | `net use z: \\your.dc.fqdn\c$\HmD /user:DLGUSER01@hello.y.dir`
17 | + When prompted, type in the DLGUSER01 password. The drive map should then succeed.
18 | + Next, copy the HmD binaries from the existing DC to your server:
19 | `robocopy z:\ C:\HmD /MIR`
20 | + Wait for the copy to end, then hunt for any error during transfert. If everything ran smoothly, unmount the drive:
21 | `net use z: /delete`
22 | + quit powershell.
23 |
24 | ## Step 3: A DC is born...
25 | + Fire-up PowerShell with admin rights (id est runas administrator).
26 | + Run the script, without the specific parameter AddDC:
27 | `cd c:\HmD`
28 | `.\invoke-helloMyDir.ps1`
29 | + The scripts deploys mandatory binaries, then it will prompt you to provide the password for the account _DLGUSER01_.
30 | + Once the domain joining is done, the script will reboot (you have to press a key first). Wait a few minutes for the joining process to fullfill its needs...
31 | + Once the reboot is done, logging back _as the domain administrator, or any other account with domain admins rights_.
32 | + > **beware**: if you use the builtin administrator account, you will not be logged in as a domain user if you do not specify the domain name (_administrator@hello.my.dir_ or _hello\administrator_ as login name.)
33 | + Fire-up PowerShell with admin rights again (id est runas administrator).
34 | + rebase yourself to c:\HmD, then run the script once more with the AddDC parameter:
35 | `cd c:\HmD`
36 | `.\invoke-helloMyDir.ps1 -addDC`
37 | + Wait for the script to proceed and write-down the DSRM password when invited to do so.
38 | + Press a key to let the server reboot.
39 |
40 | And here you are: a new DC is now present in your domain! Just reboot your primary DC or delete the HmD share as it is no more requiered.
41 |
--------------------------------------------------------------------------------
/Documentation/Step-by-Step - Deploy Hello-my-Dir.md:
--------------------------------------------------------------------------------
1 | # Step-By-Step: Deploying Hello-my-Dir
2 | This procedure will guide you through the process of deploying Hello-my-Dir.
3 |
4 | ## Step 1: planting the Seeds
5 | + Build your new windows server and assign it a static IP. the DNS client should be set to your final resolver, id est the ones to which your DC will forward unkown request - most oftenly a public DNS server like google's one.
6 | + Update your server to the latest patch from Microsoft - so to say: be up-to-date.
7 | + Download the latest HmD release package from github (https://github.com/LoicVeirman/Hello-My-Dir/releases)
8 | + Unzip the binaries to __c:\HmD__
9 | + Fire-up a powerShell console with elevated rgihts (i.e. runas admin)
10 | + Unblock the files by running the command:
11 | `get-ChildItem c:\HmD -recurse | unblock-file`
12 | + Run the script once to create the setup file:
13 | `cd c:\HmD`
14 | `.\invoke-HelloMyDir.ps1`
15 | + Take time to answer to each question accordingly to your need until it ends you back to the prompt.
16 |
17 | # Step 2: blooming of a tree
18 | + Run the script to begin the domain installation:
19 | `.\invoke-helloMyDir.ps1`
20 | + Press a key to confirm your will of building the new forest/domain, then wait until the script prompts you to write-down the DSRM password (do it, or do not but... You can reset it anyway).
21 | + Press a key once ready: the server will reboot and took some time to finalize the domain install.
22 |
23 | # Step 3: harden the wood
24 | + Logon to your DC with the Builtin _Administrator_ account
25 | + fire-up a powerShell console with elevated rgihts (i.e. runas admin)
26 | + Run the script to begin the hardening steps:
27 | `.\invoke-helloMyDir.ps1`
28 | + write-down the password generated for the _DLGUSER_ user account ; this account is able to join a computer to the domain if the computer object is preexisting in the _Computers_ container.
29 | + job's done: you're domain is ready and hardened!
30 |
--------------------------------------------------------------------------------
/Documentation/User Manual for release 1.0.0.md:
--------------------------------------------------------------------------------
1 | # HELLO MY DIR! VERSION 01.00.00
2 | ## USER MANUAL
3 |
4 | ### Welcome!
5 | Thank-you for joining-us in the active directory hardening journey!
6 | We sincerly hope this code will ease your day and enforce your basic posture regarding security assessment!
7 |
8 | With this code, you will be able to mount a new domain in a new forest and secure it by design.
9 | The code is intended to create any kind of domain (in an existing forest as a child or an independant one), but this release
10 | only cover the unique case of building a new forest - this is just a matter of testing before we update our code and enhance its ability to
11 | deliver the same in any build context!
12 |
13 | Angry to learn how this works? Then let's go deep in details!
14 |
15 | ### Prerequesites
16 | You must run the script on a system with .Net 4.8 and PowerShell 5.0.
17 | The script has been tested successfully on:
18 | > Windows Server 2022
19 | > Windows Server 2019
20 | > Windows Server 2016
21 |
22 | Be advise that our test on a 2012 R2 box has failed - however with some code arrangment, it should possible to run it.
23 |
24 | ### What does the script do?
25 | First of all, let's discuss how the script will proceed. As everything in the world, any new AD domain begin with a brand new Windows Server System - so to say, a Vanilla one.
26 |
27 | Once the script runs, it will proceed this way:
28 | 1. It will test for the presence of a file (.\Configuration\RunSetup.xml):
29 | > If the file is found, then the script will consider that you have provided the requiered information.
30 | > If the file is missing, then the script will start by asking you question relative to your new domain build and create it.
31 | > (you can update the file manually or by running again the script with the option **-Prepare**)
32 |
33 | 2. If the file was present at step 1, then the script will check if your server is a domain member:
34 | > if not, then the script will proceed to the domain installation (see step 3)
35 | > If yes, then the script *always* consider that the domain has just been installed on this server and proceed with hardening (see step 4).
36 | > (Well, you are building a new domain, isn't it? So forcefully... Take a time about it.)
37 |
38 | 3. Before runing the new forest installation, the script proceed with some extra checking and installation:
39 | > Install, if needed, the *AD-Domain-Service* windows feature and its toolset
40 | > Install, if needed, the *RSAT-AD-Tools* windows feature and its toolset
41 | > Install, if needed, the *RSAT-DNS-Server* windows feature and its toolset
42 | > Install, if needed, the *RSAT-DFS-Mgmt-Con* windows feature and its toolset
43 | > Install, if needed, the *GPMC* windows feature and its toolset
44 | > Then, the script will start the forest installation.
45 | > Once done, the script will display a random password generated for the Disaster Recovery Mode (write it down or change it later)
46 | > and ask you to press a ket before rebooting. Cofee time.
47 |
48 | 4. Once the server is ready to serve with a brand new AD on set, you'll have to login back and rerun the script. The script then detect that this is time for the hardening fest:
49 | > First, the script will remedy to Ping Castle alerts (S-ADRegistration, S-DC-SubnetMissing, S-PwdNeverExpires, P-RecycleBin, P-SchemaAdmin, P-UnprotectedOU, A-MinPwdLen, A-PreWin2000AuthenticatedUsers, A-LAPS-NOT-Installed,P-Delegated).
50 | You can have more details on each remediation here: https://www.pingcastle.com/PingCastleFiles/ad_hc_rules_list.html
51 |
52 | > Second, the script will remedy to Purple Knight alerts (Protected-Users, LDAPS-required).
53 | You can find more details on the semperis web site (https://www.semperis.com/fr/ad-security-vulnerability-assessment/).
54 |
55 | > Third, the script will add two new GPO to enforce some security default rules:
56 | >> *Default Domain Security*: replace some messy parameters from Default Domain Policy
57 | >> *Default Domain Controllers Security*: replace some deadly parameters from Default Domain Controllers Policy
58 |
59 | 5. Final step, the script will ask you to perform a last reboot, which will ensure all GPOs are properly applied.
60 |
61 | Here is it: AD secured!
62 |
63 | ### How to run the script?
64 | First, download the latest release from https://github.com/LoicVeirman/Hello-My-Dir.
65 | Then, extract the ZIP anywhere on your system - we do use to extract it to *C:\Hello-my-Dir*, but this is not mandatory.
66 | Fire-up a PowerShell Console (as administrator), and run the below commands (the script will be located in c:\Hello-My-Dir):
67 | ```PS
68 | CD C:\Hello-My-DIR
69 | .\Invoke-HelloMyDir.ps1
70 | ```
71 |
72 | You can run the configuration setup with the following command:
73 | ```PS
74 | .\Invoke-HelloMyDIR.ps1 -Prepare
75 | ```
76 |
77 | ### What will be your next step?
78 | The script is just a starting point. From there, you should add another DC (which will require a certificate for LDAPS) and brough a Tiering Model Policy.
79 | We strongly invite you considering **hardenAD**, if you do not have a Tiering Model of your own (https://hardenad.net).
80 | It will be also needed that you customize the LAPS policy (who can read, who can reset, etc).
81 |
82 | ### Troubleshooting
83 | When running, the script will always provide you with a result on each of its action:
84 | 1. **SUCCESS**: the task has been executed as expected.
85 | 2. **WARNING**: the task met an unexpected result, however this is not related to the code and should be review.
86 | 3. **ERROR**: the taks failed to execute and exit abnormally.
87 |
88 | The logging is added to the event-viewer:
89 | > Open *EventVwr.msc*
90 | > Navigate to *Applications and Services Logs*
91 | > Open the log named *HelloMyDir*
92 |
93 | Each function has its own Source, which in turn contains the output: you can then review the details of the execution. Warning and Error are equal to the output from the script, whereas Information is meant for SUCCESS.
94 |
95 | In any case, you case create a support request in our github repository.
96 |
97 | ### To conclude
98 | We sincerly hope this script will help non-AD expert to provide a reliable and secure AD to their teams or customers. Fell free to contact-us for any needs!
--------------------------------------------------------------------------------
/Documentation/User Manual for release 1.1.0.md:
--------------------------------------------------------------------------------
1 | # Hello My DIR! User Manual
2 | #### Release 01.01.00 - *Hello My DC!* distribution pack
3 |
4 | ### Welcome!
5 | Thank-you for joining-us in the active directory hardening journey!
6 | We sincerly hope this code will ease your day and enforce your basic posture regarding security assessment!
7 |
8 | With this code, you will be able to mount a new domain in a new forest and secure it by design.
9 | The code is intended to create any kind of domain (in an existing forest as a child or an independant one), but this release
10 | only cover the unique case of building a new forest - this is just a matter of testing before we update our code and enhance its ability to
11 | deliver the same in any build context!
12 |
13 | Hungry to learn how this works? Then let's go deep in details!
14 |
15 | ### What's New
16 | The version 1.1.0 add new feature to Hello My Dir:
17 | 1. A new group will be created for delegation purpose when joining a computer to the domain (*LS-DLG-DomainJoin-Extended*)
18 | 2. A new user will be created to be used as a service account to join a computer to the domain. This account is member of the new group.
19 | 3. A right delegation is set on the default computer organizational unit (CN=Computers,DC=Root,DC=DN):
20 | > Allow CreateChild and DeleteChild on all descendent computer object
21 | > Allow CreateChild and DeleteChild on all child of a computer object
22 |
23 | The right is givent to the group created with this relase (*LS-DLG-DomainJoin-Extended*) and should be considered as a Tier 0 asset in this context.
24 |
25 | ### Upgrade path
26 | Any domain deployed with the version 1.0.0 should be updated to version 1.1.0 prior to any other action. This is mandatory as the configuration file (*RunSetup.xml*) must be updated with new data and the domain be upscaled with the new objects and delegation rights. To upgrade to version 1.1.0:
27 | > Download the binaries for the version 1.1.0 (*Hello My DC!*)
28 | >
29 | > Copy the binaries to your DC; if you have kept your previous installation files, please overwrite them. If you have deleted the previous files, please write down the domain name, domain netbios name, DFL and FFL information, as it will be requested by the script.
30 | >
31 | > Run the following command:
32 | > ```PS
33 | > Invoke-HelloMyDIR.ps1 -Prepare
34 | > ```
35 | >
36 | > Follow-up the sequence to fillup the configuration file for your domain. the script will use as default value your previous input from the *RunSetup.xml* file. You can choose to rename the group and the user account following your need.
37 | >
38 | > Once done, with an account granted as Domain Admins or equivalent, run the following command to upgrade:
39 | > ```PS
40 | > Invoke-HelloMyDIR.ps1
41 | > ```
42 | >
43 | > Review the ouput to ensure that no error has araise - you can safely ignore the warning around the Group Policy Objects.
44 |
45 | ### Important notice
46 | This script is intended to deploy a new domain, hence we do consider that you do not have other account than **Administrator** to connect to a system. Because this account is member of the group *Protected Users*, it will not be possible to promote a workgroup server as a domain controller or a domain server with this edition, as the code used require NTLM. To circumvent this limitation, we have choosen to use a service account with delegation on computer's object which is able to use NTLM and is not a highly privileged account. This is also why we do promote a workgroup server as a server member before promoting it as a domain controler. Some test are on-going to bypass this limitation and be able to use Kerberos with a non-domain joined server.
47 |
48 | ### Prerequesites
49 | You must run the script on a system with .Net 4.8 and PowerShell 5.0.
50 | The script has been tested successfully on:
51 | > Windows Server 2022
52 | > Windows Server 2019
53 | > Windows Server 2016
54 |
55 | Be advise that our test on a 2012 R2 box has failed - however with some code arrangment, it should possible to run it.
56 |
57 | ### What does the script do?
58 | First of all, let's discuss how the script will proceed. As everything in the world, any new AD domain begin with a brand new Windows Server System - so to say, a Vanilla one.
59 |
60 | Once the script runs, it will proceed this way:
61 | 1. It will test for the presence of a file (.\Configuration\RunSetup.xml):
62 | > If the file is found, then the script will consider that you have provided the requiered information.
63 | > If the file is missing, then the script will start by asking you question relative to your new domain build and create it.
64 | > (you can update the file manually or by running again the script with the option **-Prepare**)
65 |
66 | 2. If the file was present at step 1, then the script will check if your server is a domain member:
67 | > if not, then the script will proceed to the domain installation (see step 3)
68 | > If yes, then the script *always* consider that the domain has just been installed on this server and proceed with hardening (see step 4).
69 | > (Well, you are building a new domain, isn't it? So forcefully... Take a time about it.)
70 |
71 | 3. Before runing the new forest installation, the script proceed with some extra checking and installation:
72 | > Install, if needed, the *AD-Domain-Service* windows feature and its toolset
73 | > Install, if needed, the *RSAT-AD-Tools* windows feature and its toolset
74 | > Install, if needed, the *RSAT-DNS-Server* windows feature and its toolset
75 | > Install, if needed, the *RSAT-DFS-Mgmt-Con* windows feature and its toolset
76 | > Install, if needed, the *GPMC* windows feature and its toolset
77 | > Then, the script will start the forest installation.
78 | > Once done, the script will display a random password generated for the Disaster Recovery Mode (write it down or change it later)
79 | > and ask you to press a ket before rebooting. Cofee time.
80 |
81 | 4. Once the server is ready to serve with a brand new AD on set, you'll have to login back and rerun the script. The script then detect that this is time for the hardening fest:
82 | > First, the script will remedy to Ping Castle alerts (S-ADRegistration, S-DC-SubnetMissing, S-PwdNeverExpires, P-RecycleBin, P-SchemaAdmin, P-UnprotectedOU, A-MinPwdLen, A-PreWin2000AuthenticatedUsers, A-LAPS-NOT-Installed,P-Delegated).
83 | You can have more details on each remediation here: https://www.pingcastle.com/PingCastleFiles/ad_hc_rules_list.html
84 |
85 | > Second, the script will remedy to Purple Knight alerts (Protected-Users, LDAPS-required).
86 | You can find more details on the semperis web site (https://www.semperis.com/fr/ad-security-vulnerability-assessment/).
87 |
88 | > Third, the script will add two new GPO to enforce some security default rules:
89 | >> *Default Domain Security*: replace some messy parameters from Default Domain Policy
90 | >> *Default Domain Controllers Security*: replace some deadly parameters from Default Domain Controllers Policy
91 |
92 | 5. Final step, the script will ask you to perform a last reboot, which will ensure all GPOs are properly applied.
93 |
94 | Here is it: AD secured!
95 |
96 | ### How to run the script?
97 | First, download the latest release from https://github.com/LoicVeirman/Hello-My-Dir.
98 | Then, extract the ZIP anywhere on your system - we do use to extract it to *C:\Hello-my-Dir*, but this is not mandatory.
99 | Fire-up a PowerShell Console (as administrator), and run the below commands (the script will be located in c:\Hello-My-Dir):
100 | ```PS
101 | CD C:\Hello-My-DIR
102 | .\Invoke-HelloMyDir.ps1
103 | ```
104 |
105 | You can rerun the configuration setup with the following command at anytime **before** the AD is built:
106 | ```PS
107 | .\Invoke-HelloMyDIR.ps1 -Prepare
108 | ```
109 |
110 | ### What will be your next step?
111 | The script is just a starting point. From there, you should add another DC (see following section) and brough a Tiering Model Policy.
112 | We strongly invite you considering **hardenAD**, if you do not have a Tiering Model of your own (https://hardenad.net).
113 | It will be also needed that you customize the LAPS policy (who can read, who can reset, etc).
114 |
115 | To assist you in adding a second DC, the version 1.1.0 has added a new parameter to promote a server as Domain Controller and secure it.
116 |
117 | ### Adding a second Domain Controler
118 | The release 1.1.0 has introduce a new parameter to assist you in adding a new DC to your domain :
119 | ```PS
120 | .\Invoke-HelloMyDIR.ps1 -AddDC
121 | ```
122 |
123 | To use this parameter, you will have to:
124 | 1. If needed, update your previous edition of the script to version 1.1.0 (see the *upgrade path* section)
125 |
126 | 2. Install your new server as a vanilla one - you can add it to your domain, but this is not mandatory. Your server should be able to resolve your domain name and to reach your running DC on mandatory port (https://learn.microsoft.com/en-us/troubleshoot/windows-server/active-directory/config-firewall-for-ad-domains-and-trusts)
127 |
128 | 3. Copy your *Hello My DIR* folder to your new system (or at least the file name **RunSetup.xml** present in the .\configuration folder from your source DC)
129 |
130 | 4. run the script:
131 | ```PS
132 | Invoke-HelloMyDIR.ps1 -AddDC
133 | ```
134 |
135 | 5. If your server was not a domain member: the script will add-it as a server member and reboot.
136 |
137 | 6. Login to the server and rerun the script:
138 | ```PS
139 | Invoke-HelloMyDIR.ps1 -AddDC
140 | ```
141 |
142 | 7. the script will the perform the following tasks in sequence:
143 | > Install mandatory binaries (ADDS, RSAT and GPMC)
144 | > Reset the computer owner to the *Domain Admins* group
145 | > Reset the computer Security Descriptor to its default values
146 | > Promote the server as a new domain controller
147 | > Add a self-signed certificate to enable LDAPS
148 | > Display the DSRM password
149 | > Perform a reboot
150 |
151 | And that's it: your new DC is promoted and ready to use!
152 |
153 | ### Troubleshooting
154 | When running, the script will always provide you with a result on each of its action:
155 | 1. **SUCCESS**: the task has been executed as expected.
156 | 2. **WARNING**: the task met an unexpected result, however this is not related to the code and should be review.
157 | 3. **ERROR**: the taks failed to execute and exit abnormally.
158 |
159 | The logging is added to the event-viewer:
160 | > Open *EventVwr.msc*
161 | > Navigate to *Applications and Services Logs*
162 | > Open the log named *HelloMyDir*
163 |
164 | Each function has its own Source, which in turn contains the output: you can then review the details of the execution. Warning and Error are equal to the output from the script, whereas Information is meant for SUCCESS.
165 |
166 | In any case, you case create a support request in our github repository.
167 |
168 | ### To conclude
169 | We sincerly hope this script will help non-AD expert to provide a reliable and secure AD to their teams or customers. Fell free to contact-us for any needs!
170 |
--------------------------------------------------------------------------------
/Documentation/what's new in 1.1.2 QF 02.md:
--------------------------------------------------------------------------------
1 | # Change list in 1.1.2 Quick Fix 02
2 | - Fixed issue with prerequesites that were not reporting properly.
3 | - Added a reboot condition when functionalities are in inconsistante state.
4 | - Added a script to resolve the "Public Network Behavior" on DC (thanks MS to never deal it for years now...)
5 |
6 | # Code improvement
7 | - a new module has been added to handle MS System known issue (module-MSfix)
8 |
9 | # Documentation improvement
10 | - a step-by-step guide has been added to build your brand new domain.
11 | - a step-by-step guide has been added to add a DC to your HmD domain.
--------------------------------------------------------------------------------
/Imports/Default Domain Controllers Security/{9947AFBC-5433-47E0-AFB8-E7CBD1D23887}/Backup.xml:
--------------------------------------------------------------------------------
1 |
2 | 01 00 04 9c 00 00 00 00 00 00 00 00 00 00 00 00 14 00 00 00 04 00 ec 00 08 00 00 00 05 02 28 00 00 01 00 00 01 00 00 00 8f fd ac ed b3 ff d1 11 b4 1d 00 a0 c9 68 f9 39 01 01 00 00 00 00 00 05 0b 00 00 00 00 00 24 00 ff 00 0f 00 01 05 00 00 00 00 00 05 15 00 00 00 7a bd f2 a5 52 e5 61 43 18 bd e5 78 00 02 00 00 00 02 24 00 ff 00 0f 00 01 05 00 00 00 00 00 05 15 00 00 00 7a bd f2 a5 52 e5 61 43 18 bd e5 78 00 02 00 00 00 02 24 00 ff 00 0f 00 01 05 00 00 00 00 00 05 15 00 00 00 7a bd f2 a5 52 e5 61 43 18 bd e5 78 07 02 00 00 00 02 14 00 94 00 02 00 01 01 00 00 00 00 00 05 09 00 00 00 00 02 14 00 94 00 02 00 01 01 00 00 00 00 00 05 0b 00 00 00 00 02 14 00 ff 00 0f 00 01 01 00 00 00 00 00 05 12 00 00 00 00 0a 14 00 ff 00 0f 00 01 01 00 00 00 00 00 03 00 00 00 00
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/Imports/Default Domain Controllers Security/{9947AFBC-5433-47E0-AFB8-E7CBD1D23887}/DomainSysvol/GPO/Machine/comment.cmtx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/Imports/Default Domain Controllers Security/{9947AFBC-5433-47E0-AFB8-E7CBD1D23887}/DomainSysvol/GPO/Machine/microsoft/windows nt/Audit/audit.csv:
--------------------------------------------------------------------------------
1 | Machine Name,Policy Target,Subcategory,Subcategory GUID,Inclusion Setting,Exclusion Setting,Setting Value
2 | ,System,Audit Credential Validation,{0cce923f-69ae-11d9-bed3-505054503030},Success,,1
3 | ,System,Audit Kerberos Authentication Service,{0cce9242-69ae-11d9-bed3-505054503030},Success and Failure,,3
4 | ,System,Audit Kerberos Service Ticket Operations,{0cce9240-69ae-11d9-bed3-505054503030},Success,,1
5 | ,System,Audit Other Account Logon Events,{0cce9241-69ae-11d9-bed3-505054503030},Success,,1
6 | ,System,Audit Application Group Management,{0cce9239-69ae-11d9-bed3-505054503030},Success,,1
7 | ,System,Audit Computer Account Management,{0cce9236-69ae-11d9-bed3-505054503030},Success,,1
8 | ,System,Audit Distribution Group Management,{0cce9238-69ae-11d9-bed3-505054503030},Success,,1
9 | ,System,Audit Other Account Management Events,{0cce923a-69ae-11d9-bed3-505054503030},Success,,1
10 | ,System,Audit Security Group Management,{0cce9237-69ae-11d9-bed3-505054503030},Success,,1
11 | ,System,Audit User Account Management,{0cce9235-69ae-11d9-bed3-505054503030},Success,,1
12 | ,System,Audit DPAPI Activity,{0cce922d-69ae-11d9-bed3-505054503030},Success,,1
13 | ,System,Audit PNP Activity,{0cce9248-69ae-11d9-bed3-505054503030},Success,,1
14 | ,System,Audit Process Creation,{0cce922b-69ae-11d9-bed3-505054503030},Success,,1
15 | ,System,Audit Process Termination,{0cce922c-69ae-11d9-bed3-505054503030},Success,,1
16 | ,System,Audit RPC Events,{0cce922e-69ae-11d9-bed3-505054503030},Success,,1
17 | ,System,Audit Token Right Adjusted,{0cce924a-69ae-11d9-bed3-505054503030},Success,,1
18 | ,System,Audit Detailed Directory Service Replication,{0cce923e-69ae-11d9-bed3-505054503030},Success and Failure,,3
19 | ,System,Audit Directory Service Access,{0cce923b-69ae-11d9-bed3-505054503030},Success and Failure,,3
20 | ,System,Audit Directory Service Changes,{0cce923c-69ae-11d9-bed3-505054503030},Success and Failure,,3
21 | ,System,Audit Directory Service Replication,{0cce923d-69ae-11d9-bed3-505054503030},Success and Failure,,3
22 | ,System,Audit Account Lockout,{0cce9217-69ae-11d9-bed3-505054503030},Success,,1
23 | ,System,Audit User / Device Claims,{0cce9247-69ae-11d9-bed3-505054503030},Success,,1
24 | ,System,Audit Group Membership,{0cce9249-69ae-11d9-bed3-505054503030},Success,,1
25 | ,System,Audit IPsec Extended Mode,{0cce921a-69ae-11d9-bed3-505054503030},Success,,1
26 | ,System,Audit IPsec Main Mode,{0cce9218-69ae-11d9-bed3-505054503030},Success,,1
27 | ,System,Audit IPsec Quick Mode,{0cce9219-69ae-11d9-bed3-505054503030},Success,,1
28 | ,System,Audit Logoff,{0cce9216-69ae-11d9-bed3-505054503030},Success,,1
29 | ,System,Audit Logon,{0cce9215-69ae-11d9-bed3-505054503030},Success,,1
30 | ,System,Audit Network Policy Server,{0cce9243-69ae-11d9-bed3-505054503030},Success,,1
31 | ,System,Audit Other Logon/Logoff Events,{0cce921c-69ae-11d9-bed3-505054503030},Success,,1
32 | ,System,Audit Special Logon,{0cce921b-69ae-11d9-bed3-505054503030},Success,,1
33 | ,System,Audit Application Generated,{0cce9222-69ae-11d9-bed3-505054503030},Success,,1
34 | ,System,Audit Certification Services,{0cce9221-69ae-11d9-bed3-505054503030},Success,,1
35 | ,System,Audit Detailed File Share,{0cce9244-69ae-11d9-bed3-505054503030},Success,,1
36 | ,System,Audit File Share,{0cce9224-69ae-11d9-bed3-505054503030},Success,,1
37 | ,System,Audit File System,{0cce921d-69ae-11d9-bed3-505054503030},Success,,1
38 | ,System,Audit Filtering Platform Connection,{0cce9226-69ae-11d9-bed3-505054503030},Success,,1
39 | ,System,Audit Filtering Platform Packet Drop,{0cce9225-69ae-11d9-bed3-505054503030},Success,,1
40 | ,System,Audit Handle Manipulation,{0cce9223-69ae-11d9-bed3-505054503030},Success,,1
41 | ,System,Audit Kernel Object,{0cce921f-69ae-11d9-bed3-505054503030},Success,,1
42 | ,System,Audit Other Object Access Events,{0cce9227-69ae-11d9-bed3-505054503030},Success,,1
43 | ,System,Audit Registry,{0cce921e-69ae-11d9-bed3-505054503030},Success,,1
44 | ,System,Audit Removable Storage,{0cce9245-69ae-11d9-bed3-505054503030},Success,,1
45 | ,System,Audit SAM,{0cce9220-69ae-11d9-bed3-505054503030},Success,,1
46 | ,System,Audit Central Access Policy Staging,{0cce9246-69ae-11d9-bed3-505054503030},Success,,1
47 | ,System,Audit Audit Policy Change,{0cce922f-69ae-11d9-bed3-505054503030},Success,,1
48 | ,System,Audit Authentication Policy Change,{0cce9230-69ae-11d9-bed3-505054503030},Success,,1
49 | ,System,Audit Authorization Policy Change,{0cce9231-69ae-11d9-bed3-505054503030},Success,,1
50 | ,System,Audit Filtering Platform Policy Change,{0cce9233-69ae-11d9-bed3-505054503030},Success,,1
51 | ,System,Audit MPSSVC Rule-Level Policy Change,{0cce9232-69ae-11d9-bed3-505054503030},Success,,1
52 | ,System,Audit Other Policy Change Events,{0cce9234-69ae-11d9-bed3-505054503030},Success,,1
53 | ,System,Audit Non Sensitive Privilege Use,{0cce9229-69ae-11d9-bed3-505054503030},Success,,1
54 | ,System,Audit Other Privilege Use Events,{0cce922a-69ae-11d9-bed3-505054503030},Success,,1
55 | ,System,Audit Sensitive Privilege Use,{0cce9228-69ae-11d9-bed3-505054503030},Success,,1
56 | ,System,Audit IPsec Driver,{0cce9213-69ae-11d9-bed3-505054503030},Success,,1
57 | ,System,Audit Other System Events,{0cce9214-69ae-11d9-bed3-505054503030},Success,,1
58 | ,System,Audit Security State Change,{0cce9210-69ae-11d9-bed3-505054503030},Success,,1
59 | ,System,Audit Security System Extension,{0cce9211-69ae-11d9-bed3-505054503030},Success,,1
60 | ,System,Audit System Integrity,{0cce9212-69ae-11d9-bed3-505054503030},Success,,1
61 |
--------------------------------------------------------------------------------
/Imports/Default Domain Controllers Security/{9947AFBC-5433-47E0-AFB8-E7CBD1D23887}/DomainSysvol/GPO/Machine/microsoft/windows nt/SecEdit/GptTmpl.inf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LoicVeirman/Hello-My-Dir/0de4dba5a16e5f94c5efd968b7220b49c8bd5a72/Imports/Default Domain Controllers Security/{9947AFBC-5433-47E0-AFB8-E7CBD1D23887}/DomainSysvol/GPO/Machine/microsoft/windows nt/SecEdit/GptTmpl.inf
--------------------------------------------------------------------------------
/Imports/Default Domain Controllers Security/{9947AFBC-5433-47E0-AFB8-E7CBD1D23887}/DomainSysvol/GPO/Machine/registry.pol:
--------------------------------------------------------------------------------
1 | PReg [ S o f t w a r e \ P o l i c i e s \ M i c r o s o f t \ W i n d o w s \ N e t w o r k P r o v i d e r \ H a r d e n e d P a t h s ; \ \ * \ S Y S V O L ; ; d ; R e q u i r e M u t u a l A u t h e n t i c a t i o n = 1 , R e q u i r e I n t e g r i t y = 1 ] [ S o f t w a r e \ P o l i c i e s \ M i c r o s o f t \ W i n d o w s \ N e t w o r k P r o v i d e r \ H a r d e n e d P a t h s ; \ \ * \ N E T L O G O N ; ; d ; R e q u i r e M u t u a l A u t h e n t i c a t i o n = 1 , R e q u i r e I n t e g r i t y = 1 ]
--------------------------------------------------------------------------------
/Imports/Default Domain Controllers Security/{9947AFBC-5433-47E0-AFB8-E7CBD1D23887}/bkupInfo.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/Imports/Default Domain Controllers Security/{9947AFBC-5433-47E0-AFB8-E7CBD1D23887}/gpreport.xml:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LoicVeirman/Hello-My-Dir/0de4dba5a16e5f94c5efd968b7220b49c8bd5a72/Imports/Default Domain Controllers Security/{9947AFBC-5433-47E0-AFB8-E7CBD1D23887}/gpreport.xml
--------------------------------------------------------------------------------
/Imports/Default Domain Security/{0B0FC9E4-A7D9-4800-9AF9-272E38C6CCCE}/Backup.xml:
--------------------------------------------------------------------------------
1 |
2 | 01 00 04 9c 00 00 00 00 00 00 00 00 00 00 00 00 14 00 00 00 04 00 ec 00 08 00 00 00 05 02 28 00 00 01 00 00 01 00 00 00 8f fd ac ed b3 ff d1 11 b4 1d 00 a0 c9 68 f9 39 01 01 00 00 00 00 00 05 0b 00 00 00 00 00 24 00 ff 00 0f 00 01 05 00 00 00 00 00 05 15 00 00 00 7a bd f2 a5 52 e5 61 43 18 bd e5 78 00 02 00 00 00 02 24 00 ff 00 0f 00 01 05 00 00 00 00 00 05 15 00 00 00 7a bd f2 a5 52 e5 61 43 18 bd e5 78 00 02 00 00 00 02 24 00 ff 00 0f 00 01 05 00 00 00 00 00 05 15 00 00 00 7a bd f2 a5 52 e5 61 43 18 bd e5 78 07 02 00 00 00 02 14 00 94 00 02 00 01 01 00 00 00 00 00 05 09 00 00 00 00 02 14 00 94 00 02 00 01 01 00 00 00 00 00 05 0b 00 00 00 00 02 14 00 ff 00 0f 00 01 01 00 00 00 00 00 05 12 00 00 00 00 0a 14 00 ff 00 0f 00 01 01 00 00 00 00 00 03 00 00 00 00
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/Imports/Default Domain Security/{0B0FC9E4-A7D9-4800-9AF9-272E38C6CCCE}/DomainSysvol/GPO/Machine/Preferences/Registry/Registry.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/Imports/Default Domain Security/{0B0FC9E4-A7D9-4800-9AF9-272E38C6CCCE}/DomainSysvol/GPO/Machine/comment.cmtx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/Imports/Default Domain Security/{0B0FC9E4-A7D9-4800-9AF9-272E38C6CCCE}/DomainSysvol/GPO/Machine/microsoft/windows nt/SecEdit/GptTmpl.inf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LoicVeirman/Hello-My-Dir/0de4dba5a16e5f94c5efd968b7220b49c8bd5a72/Imports/Default Domain Security/{0B0FC9E4-A7D9-4800-9AF9-272E38C6CCCE}/DomainSysvol/GPO/Machine/microsoft/windows nt/SecEdit/GptTmpl.inf
--------------------------------------------------------------------------------
/Imports/Default Domain Security/{0B0FC9E4-A7D9-4800-9AF9-272E38C6CCCE}/DomainSysvol/GPO/Machine/registry.pol:
--------------------------------------------------------------------------------
1 | PReg [ S O F T W A R E \ M i c r o s o f t \ W i n d o w s \ C u r r e n t V e r s i o n \ P o l i c i e s \ L A P S ; A D B a c k u p D S R M P a s s w o r d ; ; ; ] [ S O F T W A R E \ M i c r o s o f t \ W i n d o w s \ C u r r e n t V e r s i o n \ P o l i c i e s \ L A P S ; A D P a s s w o r d E n c r y p t i o n E n a b l e d ; ; ; ] [ S O F T W A R E \ M i c r o s o f t \ W i n d o w s \ C u r r e n t V e r s i o n \ P o l i c i e s \ L A P S ; B a c k u p D i r e c t o r y ; ; ; ] [ S O F T W A R E \ M i c r o s o f t \ W i n d o w s \ C u r r e n t V e r s i o n \ P o l i c i e s \ L A P S ; P a s s w o r d E x p i r a t i o n P r o t e c t i o n E n a b l e d ; ; ; ] [ S O F T W A R E \ M i c r o s o f t \ W i n d o w s \ C u r r e n t V e r s i o n \ P o l i c i e s \ L A P S ; P a s s w o r d C o m p l e x i t y ; ; ; ] [ S O F T W A R E \ M i c r o s o f t \ W i n d o w s \ C u r r e n t V e r s i o n \ P o l i c i e s \ L A P S ; P a s s w o r d L e n g t h ; ; ; ] [ S O F T W A R E \ M i c r o s o f t \ W i n d o w s \ C u r r e n t V e r s i o n \ P o l i c i e s \ L A P S ; P a s s w o r d A g e D a y s ; ; ; ] [ S O F T W A R E \ M i c r o s o f t \ W i n d o w s \ C u r r e n t V e r s i o n \ P o l i c i e s \ L A P S ; P o s t A u t h e n t i c a t i o n R e s e t D e l a y ; ; ; ] [ S O F T W A R E \ M i c r o s o f t \ W i n d o w s \ C u r r e n t V e r s i o n \ P o l i c i e s \ L A P S ; P o s t A u t h e n t i c a t i o n A c t i o n s ; ; ; ] [ S O F T W A R E \ P o l i c i e s \ M i c r o s o f t \ W i n d o w s \ P o w e r S h e l l \ M o d u l e L o g g i n g ; E n a b l e M o d u l e L o g g i n g ; ; ; ] [ S O F T W A R E \ P o l i c i e s \ M i c r o s o f t \ W i n d o w s \ P o w e r S h e l l \ M o d u l e L o g g i n g \ M o d u l e N a m e s ; * * d e l v a l s . ; ; ; ] [ S O F T W A R E \ P o l i c i e s \ M i c r o s o f t \ W i n d o w s \ P o w e r S h e l l \ M o d u l e L o g g i n g \ M o d u l e N a m e s ; * ; ; ; * ] [ S O F T W A R E \ P o l i c i e s \ M i c r o s o f t \ W i n d o w s \ P o w e r S h e l l \ S c r i p t B l o c k L o g g i n g ; E n a b l e S c r i p t B l o c k L o g g i n g ; ; ; ] [ S O F T W A R E \ P o l i c i e s \ M i c r o s o f t \ W i n d o w s \ P o w e r S h e l l \ S c r i p t B l o c k L o g g i n g ; E n a b l e S c r i p t B l o c k I n v o c a t i o n L o g g i n g ; ; ; ] [ S O F T W A R E \ P o l i c i e s \ M i c r o s o f t \ W i n d o w s N T \ D N S C l i e n t ; E n a b l e M u l t i c a s t ; ; ; ]
--------------------------------------------------------------------------------
/Imports/Default Domain Security/{0B0FC9E4-A7D9-4800-9AF9-272E38C6CCCE}/bkupInfo.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/Imports/Default Domain Security/{0B0FC9E4-A7D9-4800-9AF9-272E38C6CCCE}/gpreport.xml:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LoicVeirman/Hello-My-Dir/0de4dba5a16e5f94c5efd968b7220b49c8bd5a72/Imports/Default Domain Security/{0B0FC9E4-A7D9-4800-9AF9-272E38C6CCCE}/gpreport.xml
--------------------------------------------------------------------------------
/Imports/MS LAPS/LAPS.x64.msi:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LoicVeirman/Hello-My-Dir/0de4dba5a16e5f94c5efd968b7220b49c8bd5a72/Imports/MS LAPS/LAPS.x64.msi
--------------------------------------------------------------------------------
/Logs/.touch:
--------------------------------------------------------------------------------
1 | .
--------------------------------------------------------------------------------
/Modules/Module-Hardening/Module-Hardening.psd1:
--------------------------------------------------------------------------------
1 | #
2 | # Module manifest for module 'Module-Hardening'
3 | #
4 | # Generated by: loicveirman
5 | #
6 | # Generated on: 20/06/2024
7 | #
8 |
9 | @{
10 |
11 | # Script module or binary module file associated with this manifest.
12 | RootModule = 'Module-Hardening.psm1'
13 |
14 | # Version number of this module.
15 | ModuleVersion = '1.0.0.0'
16 |
17 | # Supported PSEditions
18 | # CompatiblePSEditions = @()
19 |
20 | # ID used to uniquely identify this module
21 | GUID = 'c2f2c291-eb6c-4582-90c4-a8bf108aab20'
22 |
23 | # Author of this module
24 | Author = 'loicveirman'
25 |
26 | # Company or vendor of this module
27 | CompanyName = 'MSSEC'
28 |
29 | # Copyright statement for this module
30 | Copyright = '(c) loicveirman. All rights reserved.'
31 |
32 | # Description of the functionality provided by this module
33 | # Description = ''
34 |
35 | # Minimum version of the PowerShell engine required by this module
36 | # PowerShellVersion = ''
37 |
38 | # Name of the PowerShell host required by this module
39 | # PowerShellHostName = ''
40 |
41 | # Minimum version of the PowerShell host required by this module
42 | # PowerShellHostVersion = ''
43 |
44 | # Minimum version of Microsoft .NET Framework required by this module. This prerequisite is valid for the PowerShell Desktop edition only.
45 | # DotNetFrameworkVersion = ''
46 |
47 | # Minimum version of the common language runtime (CLR) required by this module. This prerequisite is valid for the PowerShell Desktop edition only.
48 | # ClrVersion = ''
49 |
50 | # Processor architecture (None, X86, Amd64) required by this module
51 | # ProcessorArchitecture = ''
52 |
53 | # Modules that must be imported into the global environment prior to importing this module
54 | # RequiredModules = @()
55 |
56 | # Assemblies that must be loaded prior to importing this module
57 | # RequiredAssemblies = @()
58 |
59 | # Script files (.ps1) that are run in the caller's environment prior to importing this module.
60 | # ScriptsToProcess = @()
61 |
62 | # Type files (.ps1xml) to be loaded when importing this module
63 | # TypesToProcess = @()
64 |
65 | # Format files (.ps1xml) to be loaded when importing this module
66 | # FormatsToProcess = @()
67 |
68 | # Modules to import as nested modules of the module specified in RootModule/ModuleToProcess
69 | NestedModules = @('Module-Hardening.psm1')
70 |
71 | # Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export.
72 | FunctionsToExport = '*'
73 |
74 | # Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export.
75 | CmdletsToExport = '*'
76 |
77 | # Variables to export from this module
78 | VariablesToExport = '*'
79 |
80 | # Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export.
81 | AliasesToExport = '*'
82 |
83 | # DSC resources to export from this module
84 | # DscResourcesToExport = @()
85 |
86 | # List of all modules packaged with this module
87 | # ModuleList = @()
88 |
89 | # List of all files packaged with this module
90 | # FileList = @()
91 |
92 | # Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell.
93 | PrivateData = @{
94 |
95 | PSData = @{
96 |
97 | # Tags applied to this module. These help with module discovery in online galleries.
98 | # Tags = @()
99 |
100 | # A URL to the license for this module.
101 | # LicenseUri = ''
102 |
103 | # A URL to the main website for this project.
104 | # ProjectUri = ''
105 |
106 | # A URL to an icon representing this module.
107 | # IconUri = ''
108 |
109 | # ReleaseNotes of this module
110 | # ReleaseNotes = ''
111 |
112 | # Prerelease string of this module
113 | # Prerelease = ''
114 |
115 | # Flag to indicate whether the module requires explicit user acceptance for install/update/save
116 | # RequireLicenseAcceptance = $false
117 |
118 | # External dependent modules of this module
119 | # ExternalModuleDependencies = @()
120 |
121 | } # End of PSData hashtable
122 |
123 | } # End of PrivateData hashtable
124 |
125 | # HelpInfo URI of this module
126 | # HelpInfoURI = ''
127 |
128 | # Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix.
129 | # DefaultCommandPrefix = ''
130 |
131 | }
132 |
133 |
--------------------------------------------------------------------------------
/Modules/Module-Hardening/Module-Hardening.psm1:
--------------------------------------------------------------------------------
1 | <#
2 | THIS MODULE CONTAINS FUNCTIONS ONLY USABLE BY HELLO MY DIR TO HARDEN A DIRECTORY.
3 | #>
4 | #region DOMAIN JOIN DELEGATION
5 | Function Deploy-DomainJoinDelegation {
6 | <#
7 | .SYNOPSIS
8 | Allow a specific service account to join a computer to the domain.
9 |
10 | .DESCRIPTION
11 | Once the domain has been hardened, you can not join a computer to it if you do use an account member of "Protected Users".
12 | To circumvent this limitation, a sevice account will be created with the only permission to create a computer object in default location (CN=Computers).
13 | A group will also be created to manage delegation rights (and not assign this to a user object).
14 |
15 | .NOTES
16 | Version 01.00.00 (2024/06/20 - Creation)
17 | #>
18 | Param()
19 |
20 | #region INIT FUNCTION
21 | Test-EventLog | Out-Null
22 | $callStack = Get-PSCallStack
23 | $CalledBy = ($CallStack[1].Command -split '\.')[0]
24 | $ExitLevel = 'INFO'
25 | $DbgLog = @('START: Deploy-DomainJoinDelegation',' ',"Called by: $($CalledBy)",' ')
26 | $RunSetup = Get-XmlContent .\Configuration\RunSetup.xml
27 | #endregion
28 |
29 | #region CREATE GROUP
30 | #Create the new group for delegation puprose (it will be safe to rename it later, if needed)
31 | $GroupName = $RunSetup.Configuration.ADObjects.Groups.DomainJoin
32 | $isPresent = Get-AdObject -LdapFilter "(&(ObjectClass=group)(SamAccountName=$GroupName))"
33 | if ($null -eq $isPresent)
34 | {
35 | Try
36 | {
37 | [void](New-ADGroup -Description "Group to join a computer to the domain (allowed to create the object)" `
38 | -DisplayName $GroupName -GroupCategory Security -GroupScope DomainLocal `
39 | -Name $GroupName -SamAccountName $GroupName)
40 | $DbgLog += @(' ',"Group '$GroupName' successfully created.")
41 | }
42 | Catch
43 | {
44 | $DbgLog += @(' ',"Failed to create the group '$GroupName'!","Error: $($_.ToString())")
45 | $ExitLevel = 'Error'
46 | }
47 | }
48 | Else
49 | {
50 | $DbgLog += @(' ',"Group '$GroupName' already exist (no change).")
51 | }
52 | #endregion
53 |
54 | #region CREATE USER
55 | $randomSMpwd = New-LurchPassphrase
56 | $UserName = $RunSetup.Configuration.ADObjects.Users.DomainJoin
57 | $isPresent = Get-AdObject -LdapFilter "(&(ObjectClass=user)(SamAccountName=$UserName))"
58 | if ($null -eq $isPresent)
59 | {
60 | Try
61 | {
62 | [void](New-ADUser -Description "DLGUSER01 - Delegated User (domain joining)" `
63 | -Name $UserName -DisplayName $UserName `
64 | -SamAccountName $UserName -accountNotDelegated 1 `
65 | -AccountPassword (ConvertTo-SecureString -AsPlainText $randomSMpwd -Force) `
66 | -Enabled 1 -GivenName "Delegate" -Surname "USER 01" -KerberosEncryptionType AES128 `
67 | -TrustedForDelegation 0 -UserPrincipalName "$Username@$((Get-AdDomain).DnsRoot)")
68 | $DbgLog += @(' ',"User '$Username' successfully created.")
69 |
70 | # Show password to user
71 | Add-Type -AssemblyName System.Windows.Forms
72 | [void]([System.Windows.Forms.MessageBox]::Show("$($UserName) password: $($randomSMpwd)","Warning"))
73 | }
74 | Catch
75 | {
76 | $DbgLog += @(' ',"Failed to create the user '$UserName'!","Error: $($_.ToString())")
77 | $ExitLevel = 'Error'
78 | }
79 | }
80 | Else
81 | {
82 | $DbgLog += @(' ',"User '$UserName' already exist (no change).")
83 | }
84 | #endregion
85 |
86 | #region ADD USER TO GROUPS
87 | $psoXml = Get-XmlContent .\Configuration\DomainSettings.xml
88 | $GroupList = @((Select-Xml $psoXml -XPath "//*/PSO[@Ref='PsoSvcStd']" | Select-Object -ExpandProperty Node).Name, $GroupName)
89 |
90 | foreach ($Group in $GroupList)
91 | {
92 | $isMember = (Get-AdGroupMember $Group).SamAccountName -contains $UserName
93 | if ($isMember)
94 | {
95 | $DbgLog += @(' ',"User $Username is already member of $Group.")
96 | }
97 | Else
98 | {
99 | Try
100 | {
101 | [void](Add-AdGroupMember -Identity $Group -Members $UserName -ErrorAction Stop)
102 | $DbgLog += @(' ',"User $Username has been added to the group $group.")
103 | }
104 | Catch
105 | {
106 | $DbgLog += @(' ',"Failed to add $Username to the group $group!","Error: $($_.ToString())")
107 | $ExitLevel = "Error"
108 | }
109 | }
110 | }
111 | #endregion
112 |
113 | #region SET DELEGATION
114 | $Container = (Get-ADDomain).ComputersContainer
115 | Try
116 | {
117 | Push-Location AD:
118 |
119 | $inheritanceguid = New-Object Guid 00000000-0000-0000-0000-000000000000
120 | $Objectguid = New-Object Guid bf967a86-0de6-11d0-a285-00aa003049e2
121 | $group = Get-ADGroup $GroupName
122 | $SID = New-Object System.Security.Principal.SecurityIdentifier $($group.SID)
123 | $identity = [System.Security.Principal.IdentityReference] $SID
124 | $adRights = [System.DirectoryServices.ActiveDirectoryRights] "CreateChild, DeleteChild"
125 | $inheritanceType = [System.DirectoryServices.ActiveDirectorySecurityInheritance] "All"
126 | $type = [System.Security.AccessControl.AccessControlType] "Allow"
127 | $Parameters = $identity, $adRights, $type, $Objectguid, $inheritanceType, $inheritanceguid
128 | $ace = New-Object System.DirectoryServices.ActiveDirectoryAccessRule($Parameters)
129 | $acl = Get-Acl "AD:\$Container" -ErrorAction Stop
130 | $acl.AddAccessRule($ace)
131 | Set-Acl -AclObject $acl -Path "AD:\$Container" -ErrorAction Stop
132 |
133 | $inheritanceguid = New-Object Guid bf967a86-0de6-11d0-a285-00aa003049e2
134 | $Objectguid = New-Object Guid 00000000-0000-0000-0000-000000000000
135 | $group = Get-ADGroup $GroupName
136 | $SID = New-Object System.Security.Principal.SecurityIdentifier $($group.SID)
137 | $identity = [System.Security.Principal.IdentityReference] $SID
138 | $adRights = [System.DirectoryServices.ActiveDirectoryRights] "CreateChild, DeleteChild"
139 | $inheritanceType = [System.DirectoryServices.ActiveDirectorySecurityInheritance] "Descendents"
140 | $type = [System.Security.AccessControl.AccessControlType] "Allow"
141 | $Parameters = $identity, $adRights, $type, $Objectguid, $inheritanceType, $inheritanceguid
142 | $ace = New-Object System.DirectoryServices.ActiveDirectoryAccessRule($Parameters)
143 | $acl = Get-Acl "AD:\$Container" -ErrorAction Stop
144 | $acl.AddAccessRule($ace)
145 | Set-Acl -AclObject $acl -Path "AD:\$Container" -ErrorAction Stop
146 |
147 | Pop-Location
148 |
149 | $DbgLog += @(' ',"Successfully delegated rights on computer object to $GroupName at $Container.")
150 | }
151 | Catch
152 | {
153 | $DbgLog += @(' ',"Failed to delegate rights on computer object to $GroupName at $Container!","Error: $($_.ToString())")
154 | $ExitLevel = "Error"
155 | }
156 | #endregion
157 |
158 | #region RETURN RESULT
159 | Write-ToEventLog $ExitLevel $DbgLog
160 | Return $ExitLevel
161 | #endregion
162 | }
163 | #endregion
--------------------------------------------------------------------------------
/Modules/Module-HmD/Module-HmD.psd1:
--------------------------------------------------------------------------------
1 | #
2 | # Module manifest for module 'Module-HmD'
3 | #
4 | # Generated by: Loic VEIRMAN (MSSec)
5 | #
6 | # Generated on: 13/05/2024
7 | #
8 |
9 | @{
10 |
11 | # Script module or binary module file associated with this manifest.
12 | RootModule = 'Module-HmD.psm1'
13 |
14 | # Version number of this module.
15 | ModuleVersion = '1.0.0.0'
16 |
17 | # Supported PSEditions
18 | # CompatiblePSEditions = @()
19 |
20 | # ID used to uniquely identify this module
21 | GUID = 'd1eb03e2-417d-4b3d-8911-4194d7037dbf'
22 |
23 | # Author of this module
24 | Author = 'Loic VEIRMAN (MSSec)'
25 |
26 | # Company or vendor of this module
27 | CompanyName = 'MSSec'
28 |
29 | # Copyright statement for this module
30 | Copyright = '(c) Loic VEIRMAN (MSSec). All rights reserved.'
31 |
32 | # Description of the functionality provided by this module
33 | Description = 'This module is intended to manage Unique Function used by Hello my Dir.'
34 |
35 | # Minimum version of the PowerShell engine required by this module
36 | PowerShellVersion = '5.0'
37 |
38 | # Name of the PowerShell host required by this module
39 | # PowerShellHostName = ''
40 |
41 | # Minimum version of the PowerShell host required by this module
42 | # PowerShellHostVersion = ''
43 |
44 | # Minimum version of Microsoft .NET Framework required by this module. This prerequisite is valid for the PowerShell Desktop edition only.
45 | # DotNetFrameworkVersion = ''
46 |
47 | # Minimum version of the common language runtime (CLR) required by this module. This prerequisite is valid for the PowerShell Desktop edition only.
48 | # ClrVersion = ''
49 |
50 | # Processor architecture (None, X86, Amd64) required by this module
51 | # ProcessorArchitecture = ''
52 |
53 | # Modules that must be imported into the global environment prior to importing this module
54 | # RequiredModules = @()
55 |
56 | # Assemblies that must be loaded prior to importing this module
57 | # RequiredAssemblies = @()
58 |
59 | # Script files (.ps1) that are run in the caller's environment prior to importing this module.
60 | # ScriptsToProcess = @()
61 |
62 | # Type files (.ps1xml) to be loaded when importing this module
63 | # TypesToProcess = @()
64 |
65 | # Format files (.ps1xml) to be loaded when importing this module
66 | # FormatsToProcess = @()
67 |
68 | # Modules to import as nested modules of the module specified in RootModule/ModuleToProcess
69 | NestedModules = @('Module-HmD.psm1')
70 |
71 | # Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export.
72 | FunctionsToExport = '*'
73 |
74 | # Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export.
75 | CmdletsToExport = '*'
76 |
77 | # Variables to export from this module
78 | VariablesToExport = '*'
79 |
80 | # Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export.
81 | AliasesToExport = '*'
82 |
83 | # DSC resources to export from this module
84 | # DscResourcesToExport = @()
85 |
86 | # List of all modules packaged with this module
87 | # ModuleList = @()
88 |
89 | # List of all files packaged with this module
90 | # FileList = @()
91 |
92 | # Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell.
93 | PrivateData = @{
94 |
95 | PSData = @{
96 |
97 | # Tags applied to this module. These help with module discovery in online galleries.
98 | # Tags = @()
99 |
100 | # A URL to the license for this module.
101 | # LicenseUri = ''
102 |
103 | # A URL to the main website for this project.
104 | # ProjectUri = ''
105 |
106 | # A URL to an icon representing this module.
107 | # IconUri = ''
108 |
109 | # ReleaseNotes of this module
110 | ReleaseNotes = 'Module Creation'
111 |
112 | # Prerelease string of this module
113 | # Prerelease = ''
114 |
115 | # Flag to indicate whether the module requires explicit user acceptance for install/update/save
116 | # RequireLicenseAcceptance = $false
117 |
118 | # External dependent modules of this module
119 | # ExternalModuleDependencies = @()
120 |
121 | } # End of PSData hashtable
122 |
123 | } # End of PrivateData hashtable
124 |
125 | # HelpInfo URI of this module
126 | # HelpInfoURI = ''
127 |
128 | # Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix.
129 | # DefaultCommandPrefix = ''
130 |
131 | }
132 |
133 |
--------------------------------------------------------------------------------
/Modules/Module-Logs/Module-Logs.psd1:
--------------------------------------------------------------------------------
1 | #
2 | # Module manifest for module 'Module-Logs'
3 | #
4 | # Generated by: Loic VEIRMAN (MSSec)
5 | #
6 | # Generated on: 09/05/2024
7 | #
8 |
9 | @{
10 |
11 | # Script module or binary module file associated with this manifest.
12 | RootModule = 'Module-Logs.psm1'
13 |
14 | # Version number of this module.
15 | ModuleVersion = '1.0.0.0'
16 |
17 | # Supported PSEditions
18 | # CompatiblePSEditions = @()
19 |
20 | # ID used to uniquely identify this module
21 | GUID = '68bf2f67-dd90-4af5-a4ec-4a2eace5a48c'
22 |
23 | # Author of this module
24 | Author = 'Loic VEIRMAN (MSSec)'
25 |
26 | # Company or vendor of this module
27 | CompanyName = 'MSSec'
28 |
29 | # Copyright statement for this module
30 | Copyright = '(c) Loic VEIRMAN (MSSec). All rights reserved.'
31 |
32 | # Description of the functionality provided by this module
33 | Description = 'This module is intended to manage log entries to a file or the eventViewer'
34 |
35 | # Minimum version of the PowerShell engine required by this module
36 | PowerShellVersion = '5.1'
37 |
38 | # Name of the PowerShell host required by this module
39 | # PowerShellHostName = ''
40 |
41 | # Minimum version of the PowerShell host required by this module
42 | # PowerShellHostVersion = ''
43 |
44 | # Minimum version of Microsoft .NET Framework required by this module. This prerequisite is valid for the PowerShell Desktop edition only.
45 | # DotNetFrameworkVersion = ''
46 |
47 | # Minimum version of the common language runtime (CLR) required by this module. This prerequisite is valid for the PowerShell Desktop edition only.
48 | ClrVersion = '4.0'
49 |
50 | # Processor architecture (None, X86, Amd64) required by this module
51 | # ProcessorArchitecture = ''
52 |
53 | # Modules that must be imported into the global environment prior to importing this module
54 | # RequiredModules = @("Module-Logs.psm1")
55 |
56 | # Assemblies that must be loaded prior to importing this module
57 | # RequiredAssemblies = @()
58 |
59 | # Script files (.ps1) that are run in the caller's environment prior to importing this module.
60 | # ScriptsToProcess = @()
61 |
62 | # Type files (.ps1xml) to be loaded when importing this module
63 | # TypesToProcess = @()
64 |
65 | # Format files (.ps1xml) to be loaded when importing this module
66 | # FormatsToProcess = @()
67 |
68 | # Modules to import as nested modules of the module specified in RootModule/ModuleToProcess
69 | # NestedModules = @('Module-Logs')
70 |
71 | # Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export.
72 | FunctionsToExport = @("Write-toEventLog","Test-EventLog","Write-DebugLog","Export-DebugLog")
73 |
74 | # Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export.
75 | CmdletsToExport = @("Write-toEventLog","Test-EventLog","Write-DebugLog","Export-DebugLog")
76 |
77 | # Variables to export from this module
78 | # VariablesToExport = @()
79 |
80 | # Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export.
81 | AliasesToExport = @('wev','tev','wdb','edb')
82 |
83 | # DSC resources to export from this module
84 | # DscResourcesToExport = @()
85 |
86 | # List of all modules packaged with this module
87 | # ModuleList = @()
88 |
89 | # List of all files packaged with this module
90 | # FileList = @()
91 |
92 | # Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell.
93 | PrivateData = @{
94 |
95 | PSData = @{
96 |
97 | # Tags applied to this module. These help with module discovery in online galleries.
98 | # Tags = @()
99 |
100 | # A URL to the license for this module.
101 | # LicenseUri = ''
102 |
103 | # A URL to the main website for this project.
104 | # ProjectUri = ''
105 |
106 | # A URL to an icon representing this module.
107 | # IconUri = ''
108 |
109 | # ReleaseNotes of this module
110 | ReleaseNotes = 'Module Creation'
111 |
112 | # Prerelease string of this module
113 | # Prerelease = ''
114 |
115 | # Flag to indicate whether the module requires explicit user acceptance for install/update/save
116 | # RequireLicenseAcceptance = $false
117 |
118 | # External dependent modules of this module
119 | # ExternalModuleDependencies = @()
120 |
121 | } # End of PSData hashtable
122 |
123 | } # End of PrivateData hashtable
124 |
125 | # HelpInfo URI of this module
126 | # HelpInfoURI = ''
127 |
128 | # Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix.
129 | # DefaultCommandPrefix = ''
130 | }
131 |
132 |
--------------------------------------------------------------------------------
/Modules/Module-Logs/Module-Logs.psm1:
--------------------------------------------------------------------------------
1 | <#
2 | THIS MODULE CONTAINS FUNCTIONS RELATED TO LOGGING PURPOSE (FILE AND EVENT LOG)
3 | #>
4 | Function Write-toEventLog {
5 | <#
6 | .SYNOPSIS
7 | This function write log to the event viewer.
8 |
9 | .DESCRIPTION
10 | This function write log to the event viewer and use parameter from ScriptSettings.xml ().
11 |
12 | .PARAMETER EventType
13 | Type of event to report (information,warning or error).
14 |
15 | .PARAMETER EventMsg
16 | Array with all the text to append to the message.
17 |
18 | .NOTES
19 | Version: 01.000.000 -- Loic VEIRMAN (MSSec)
20 | History: 2024/05/02 -- Script creation.
21 | #>
22 |
23 | [Alias("wev")]
24 | [CmdletBinding()]
25 | param (
26 | [Parameter(Position = 0)]
27 | [ValidateSet('INFO', 'INFORMATION', 'WARNING', 'ERROR')]
28 | [AllowNull()]
29 | [string]$EventType,
30 |
31 | [Parameter(Mandatory, Position = 1)]
32 | [ValidateNotNullorEmpty()]
33 | [array]$EventMsg
34 | )
35 |
36 | # Get psStack Caller Name
37 | $callStack = Get-PSCallStack
38 | $EventSrc = ($CallStack[1].Command -split '\.')[0]
39 |
40 | # Fixed value from $EventType
41 | $EventID = @{'Information' = 0; 'Warning' = 1; 'Error' = 2 }
42 |
43 | # Load xml setting file for configuration data
44 | Try {
45 | $xmlSettings = [xml](Get-Content .\Configuration\ScriptSettings.xml -Encoding utf8 -ErrorAction Stop)
46 | }
47 | Catch {
48 | # fatal error.
49 | Exit 2
50 | }
51 |
52 | # Initialize Event Data with fixed value (adapt to your script)
53 | $Prefix = $xmlSettings.Settings.Logging.Prefix
54 | $EventSrc = "$Prefix$EventSrc"
55 |
56 | # Select adapted information for the event
57 | if ($null -eq $EventType -or $EventType -eq 'INFO') {
58 | $EventType = "Information"
59 | }
60 |
61 | # Translate array to string
62 | [String]$Message = ""
63 |
64 | foreach ($input in $EventMsg) {
65 | $Message += "$($input)`n"
66 | }
67 |
68 | # Write to EventLog. If it failed, then output to a text file (append mode)
69 | Try {
70 | [System.Diagnostics.EventLog]::WriteEntry($EventSrc, $Message, $EventType, $EventID.$EventType)
71 | }
72 | Catch {
73 | foreach ($line in ($Message -split '`n')) {
74 | "$(Get-Date -UFormat);EventType;$Line" | Out-File "$EventSrc.log" -Encoding utf8 -Append
75 | }
76 | }
77 | }
78 |
79 | Function Test-EventLog {
80 | <#
81 | .SYNOPSIS
82 | Check if the source and the log file are present on this system.
83 |
84 | .DESCRIPTION
85 | This function will prepare the system event log with the appropriate source.
86 | The Event Log File name will be retrieved from .\Configuration\config.Xml file:
87 | The Event Log Source will be the name of the script calling this function (hence, you need to call this code each time you run a function or script).
88 |
89 | This function is the only one to output a debug log in .\Logs\Test-EventLog.dbg
90 |
91 | .NOTES
92 | Version: 01.000.000 -- Loic VEIRMAN (MSSec)
93 | History: 2024/05/02 -- Script creation.
94 | #>
95 | [Alias("tev")]
96 | [CmdletBinding()]
97 | Param()
98 |
99 | # First, prepare a debug log.
100 | $DbgFile = '{0}.dbg' -f $MyInvocation.MyCommand
101 | $DbgMsg = @()
102 |
103 | # Init log
104 | $DbgMsg += Write-DebugLog -Initialize
105 |
106 | # Get File Name and Default Source to be used on creation (if needed)
107 | Try {
108 | $xmlConfig = [xml](Get-Content .\Configuration\ScriptSettings.xml -Encoding utf8 -ErrorAction Stop)
109 | $DbgMsg += Write-DebugLog INFO ".\Configuration\ScriptSettings.xml loaded successfully."
110 | }
111 | Catch {
112 | # Fatal error: script leaves.
113 | $DbgMsg += Write-DebugLog ERROR @("Failed to load .\Configuration\ScriptSettings.xml.", "Script exits with code 2")
114 | $DbgMsg += Write-DebugLog -Conclude
115 | Export-DebugLog -Target .\Logs\$DbgFile -LogData $DbgMsg
116 | return $false
117 | }
118 |
119 | $LogName = $xmlConfig.Settings.Logging.Name
120 | $LogSource = "$($xmlConfig.Settings.Logging.Prefix)$(((Get-PSCallStack)[1].Command -split '\.')[0])"
121 |
122 | if ($null -eq $Logname -or $null -eq $LogSource) {
123 | # Failed: one of the value is not properly set, hence the test will fail. Leaving the script.
124 | $DbgMsg += Write-DebugLog ERROR @("At least one value is null:", " [LogName]: $($LogName)", " [LogSource]: $($LogSource)", "Script exits with code 2")
125 | $DbgMsg += Write-DebugLog -Conclude
126 | Export-DebugLog -Target .\Logs\$DbgFile -LogData $DbgMsg
127 | return $false
128 | }
129 |
130 | # Test if the Event Log file exist
131 | if (-not($(Get-EventLog -List | Where-Object { $_.Log -eq $LogName }))) {
132 | # Create log file
133 | $DbgMsg += Write-DebugLog WARNING "The log file '$LogName' does not exists."
134 | Try {
135 | [System.Diagnostics.EventLog]::CreateEventSource($($xmlConfig.Settings.Logging.Prefix), $LogName)
136 | $DbgMsg += Write-DebugLog INFO "The log file '$LogName' has been created successfully."
137 | }
138 | Catch {
139 | # Failed: could not create the file. Leaving the script.
140 | $DbgMsg += Write-DebugLog ERROR @("Could not create the log file $($LogName)", "Script exits with code 2")
141 | $DbgMsg += Write-DebugLog -Conclude
142 | Export-DebugLog -Target .\Logs\$DbgFile -LogData $DbgMsg
143 | return $false
144 | }
145 | }
146 | Else {
147 | # Log exists
148 | $DbgMsg += Write-DebugLog INFO "The log file '$LogName' already exists."
149 | }
150 |
151 | # Test if the event source exist in the event log
152 | if ([System.Diagnostics.EventLog]::SourceExists($LogSource)) {
153 | # The source exists, we however need to ensure that it is linked to our log
154 | if (-not([System.Diagnostics.EventLog]::LogNameFromSourceName($LogSource, '.') -contains $LogName)) {
155 | # Source not linked to the log, we will link it.
156 | Try {
157 | [System.Diagnostics.EventLog]::CreateEventSource($LogSource, $LogName)
158 | $DbgMsg += Write-DebugLog INFO "The source '$LogSource' is now linked to the log '$LogName'."
159 | }
160 | Catch {
161 | # Failed: could not link the source. Leaving the script.
162 | $DbgMsg += Write-DebugLog ERROR @("Could not link the source '$LogSource' to the log $($LogName)", "Script exits with code 2")
163 | $DbgMsg += Write-DebugLog -Conclude
164 | Export-DebugLog -Target .\Logs\$DbgFile -LogData $DbgMsg
165 | return $false
166 | }
167 | }
168 | Else {
169 | # Alles gut.
170 | $DbgMsg += Write-DebugLog INFO "The source '$LogSource' is already linked to the log '$LogName'."
171 | }
172 | }
173 | Else {
174 | $DbgMsg += Write-DebugLog WARNING "The source $($LogSource) does noy exists in the $($Logname) event log."
175 |
176 | # Trying to add the new source
177 | Try {
178 | [System.Diagnostics.EventLog]::CreateEventSource($LogSource, $LogName)
179 | $DbgMsg += Write-DebugLog INFO "The source '$LogSource' is now linked to the log '$LogName'."
180 | }
181 | Catch {
182 | # Failed: could not link the source. Leaving the script.
183 | $DbgMsg += Write-DebugLog ERROR @("Could not link the source '$LogSource' to the log $($LogName)", "Script exits with code 2")
184 | $DbgMsg += Write-DebugLog -Conclude
185 | Export-DebugLog -Target .\Logs\$DbgFile -LogData $DbgMsg
186 | return $false
187 | }
188 | }
189 |
190 | # End log
191 | $DbgMsg += Write-DebugLog -Conclude
192 |
193 | # Write log to file.
194 | Export-DebugLog -Target .\Logs\$DbgFile -LogData $DbgMsg
195 |
196 | # Exit
197 | return $True
198 | }
199 |
200 | Function Write-DebugLog {
201 | <#
202 | .SYNOPSIS
203 | Grab debug text and return it as a formated log text as string.
204 |
205 | .DESCRIPTION
206 | The function ensure that text log are always formated the same way. Accept array as input (beware of timeStamp that will remain the same).
207 |
208 | .PARAMETER Criticity
209 | Define the debug text criticity level:
210 | > information: simple trace to better understand what the script was doing
211 | > warning....: something appends that is related to the script and will make it do a specific choice for the next step.
212 | > error......: something went bad and the script did not proceed as expected.
213 |
214 | .PARAMETER DebugLog
215 | An array with the text to append to the log file.
216 |
217 | .PARAMETER Iinitialize
218 | Instruct the function to add the START header.
219 | If specified with the -Conclude parameter, then only Initialize will be used.
220 |
221 | .PARAMETER Conclude
222 | Instruct the function to add the END footer.
223 | If specified with the -Initialize parameter, the Conclude will not be used.
224 |
225 | .NOTES
226 | Version: 01.000.000 -- Loic VEIRMAN (MSSec)
227 | History: 2024/05/02 -- Script creation.
228 | #>
229 | [Alias("wdb")]
230 | [CmdletBinding(DefaultParameterSetName = 'ADDLOG')]
231 | Param(
232 | [Parameter(ParameterSetName = 'ADDLOG')]
233 | [Parameter(Position = 0)]
234 | [ValidateSet('INFO', 'WARNING', 'ERROR')]
235 | [ValidateNotNullorEmpty()]
236 | [String]
237 | $Criticity,
238 |
239 | [Parameter(ParameterSetName = 'ADDLOG')]
240 | [Parameter(Position = 1)]
241 | [ValidateNotNullorEmpty()]
242 | [Array]
243 | $DebugLog,
244 |
245 | [Parameter(ParameterSetName = 'HEARDER')]
246 | [Parameter(Position = 2)]
247 | [ValidateNotNullorEmpty()]
248 | [Switch]
249 | $Initialize,
250 |
251 | [Parameter(ParameterSetName = 'FOOTER')]
252 | [Parameter(Position = 3)]
253 | [ValidateNotNullorEmpty()]
254 | [Switch]
255 | $Conclude
256 | )
257 | # Catching timestamp
258 | $timeStamp = Get-Date -Format 'yyyy/MM/dd hh:mm:ss '
259 |
260 | # Use case 1: add a header (START)
261 | If ($Initialize) {
262 | $result = @()
263 | $result += "$($timeStamp)#####"
264 | $result += "$($timeStamp)##### SCRIPT START"
265 | $result += "$($timeStamp)#####"
266 | }
267 |
268 | # Use case 2: add a footer (END)
269 | if ($Conclude -and -not($Initialize)) {
270 | $result = @()
271 | $result += "$($timeStamp)#####"
272 | $result += "$($timeStamp)##### SCRIPT END"
273 | $result += "$($timeStamp)#####"
274 | $result += "$($timeStamp)"
275 | }
276 |
277 | # Use case 3: append log.
278 | if (-not($Initialize -or $Conclude)) {
279 | # Initialize result
280 | $result = @()
281 |
282 | # Translating Criticity
283 | switch ($Criticity) {
284 | 'INFO' { $Crit = ' INF ' }
285 | 'WARNING' { $Crit = ' WNG ' }
286 | 'CRITICITY' { $Crit = ' ERR ' }
287 | }
288 |
289 | # Translating text
290 | foreach ($logLine in $DebugLog) {
291 | $result += "$($timeStamp)$($Crit)$($logLine)"
292 | }
293 | }
294 |
295 | # Sending back result.
296 | return $result
297 | }
298 |
299 | Function Export-DebugLog {
300 | <#
301 | .SYNOPSIS
302 | Write a debug log to a text file.
303 |
304 | .DESCRIPTION
305 | Write a debug log to a file and ensure that the file is no more that 500 lines.
306 |
307 | .PARAMETER Target
308 | The path and name were to write the file.
309 |
310 | .PARAMETER LogData
311 | The data to append to the file.
312 |
313 | .NOTES
314 | Version: 01.000.000 -- Loic VEIRMAN (MSSec)
315 | History: 2024/05/02 -- Script creation.
316 | #>
317 | [Alias("edb")]
318 | Param(
319 | [Parameter(mandatory, position = 0)]
320 | [System.IO.FileInfo]
321 | $Target,
322 |
323 | [Parameter(Mandatory, position = 1)]
324 | [array]
325 | $LogData
326 | )
327 |
328 | # Append new lines
329 | $LogData | Out-File $Target -Append
330 |
331 | # Keep only 1 000 lines max.
332 | $RotateLog = Get-Content $Target -Tail 1000
333 | $RotateLog | Out-File $Target -Force
334 | }
335 |
336 | #Export-ModuleMember -Function Write-toEventLog,Write-DebugLog,Test-EventLog,Export-DebugLog
--------------------------------------------------------------------------------
/Modules/Module-MSfix/Module-MSfix.psd1:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LoicVeirman/Hello-My-Dir/0de4dba5a16e5f94c5efd968b7220b49c8bd5a72/Modules/Module-MSfix/Module-MSfix.psd1
--------------------------------------------------------------------------------
/Modules/Module-MSfix/Module-MSfix.psm1:
--------------------------------------------------------------------------------
1 | Function Repair-NlaSvcOnDC
2 | {
3 | <#
4 | .SYNOPSIS
5 | Fix Network Profile issue detected as public on DC.
6 |
7 | .DESCRIPTION
8 | When a DC starts, the NLASVC service is not properly detecting the network profile as Domain and fallback to the Public one.
9 | This script operate a change to the Network Location Awareness services to ensure that detection will works as expected.
10 |
11 | .NOTES
12 | Version 1.0.0
13 | Author Bastion PEREZ
14 | #>
15 | $serviceName = "nlasvc"
16 | $desiredDependencies = @("DNS")
17 |
18 | # test if dependency exist
19 | foreach ($dependency in $desiredDependencies) {
20 | if (-not (Get-Service $dependency -ErrorAction SilentlyContinue)) {
21 | return
22 | }
23 | }
24 |
25 | # Fetch current dependencies from the registry
26 | $currentDependencies = Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\$serviceName" -Name "DependOnService" -ErrorAction SilentlyContinue | Select-Object -ExpandProperty DependOnService
27 |
28 | # Convert current dependencies to an array if they exist
29 | if ($null -eq $currentDependencies) {
30 | $currentDependencies = @()
31 | }
32 | elseif (-not ($currentDependencies -is [array])) {
33 | $currentDependencies = @($currentDependencies)
34 | }
35 |
36 | # Determine which dependencies are missing
37 | $missingDependencies = $desiredDependencies | Where-Object { $_ -notin $currentDependencies }
38 |
39 | # If there are any missing dependencies, add them
40 | $asFailed = $false
41 | if ($missingDependencies.Count -gt 0) {
42 | $newDependencies = $currentDependencies + $missingDependencies
43 | Try {
44 | [void](Set-ItemProperty -Type MultiString -Path "HKLM:\SYSTEM\CurrentControlSet\Services\$serviceName" -Name "DependOnService" -Value $newDependencies)
45 | }
46 | Catch {
47 | $asFailed = $True
48 | }
49 | }
50 |
51 | # return result
52 | if ($asFailed) {
53 | $returnCode = "Error"
54 | }
55 | Else {
56 | $returnCode = "Info"
57 | }
58 | return $returnCode
59 | }
60 |
--------------------------------------------------------------------------------
/Modules/Module-PC_3-2-0-1/Module-PC_3-2-0-1.psd1:
--------------------------------------------------------------------------------
1 | #
2 | # Module manifest for module 'Module-PC_3-2-0-1'
3 | #
4 | # Generated by: Loic VEIRMAN
5 | #
6 | # Generated on: 09/06/2024
7 | #
8 |
9 | @{
10 |
11 | # Script module or binary module file associated with this manifest.
12 | RootModule = 'Module-PC_3-2-0-1.psm1'
13 |
14 | # Version number of this module.
15 | ModuleVersion = '1.0.0.0'
16 |
17 | # Supported PSEditions
18 | # CompatiblePSEditions = @()
19 |
20 | # ID used to uniquely identify this module
21 | GUID = 'c12bd1a9-c544-4c79-affe-6ed8689700f4'
22 |
23 | # Author of this module
24 | Author = 'Loic VEIRMAN'
25 |
26 | # Company or vendor of this module
27 | CompanyName = 'Unknown'
28 |
29 | # Copyright statement for this module
30 | Copyright = '(c) Loic VEIRMAN. All rights reserved.'
31 |
32 | # Description of the functionality provided by this module
33 | # Description = ''
34 |
35 | # Minimum version of the PowerShell engine required by this module
36 | # PowerShellVersion = ''
37 |
38 | # Name of the PowerShell host required by this module
39 | # PowerShellHostName = ''
40 |
41 | # Minimum version of the PowerShell host required by this module
42 | # PowerShellHostVersion = ''
43 |
44 | # Minimum version of Microsoft .NET Framework required by this module. This prerequisite is valid for the PowerShell Desktop edition only.
45 | # DotNetFrameworkVersion = ''
46 |
47 | # Minimum version of the common language runtime (CLR) required by this module. This prerequisite is valid for the PowerShell Desktop edition only.
48 | # ClrVersion = ''
49 |
50 | # Processor architecture (None, X86, Amd64) required by this module
51 | # ProcessorArchitecture = ''
52 |
53 | # Modules that must be imported into the global environment prior to importing this module
54 | # RequiredModules = @()
55 |
56 | # Assemblies that must be loaded prior to importing this module
57 | # RequiredAssemblies = @()
58 |
59 | # Script files (.ps1) that are run in the caller's environment prior to importing this module.
60 | # ScriptsToProcess = @()
61 |
62 | # Type files (.ps1xml) to be loaded when importing this module
63 | # TypesToProcess = @()
64 |
65 | # Format files (.ps1xml) to be loaded when importing this module
66 | # FormatsToProcess = @()
67 |
68 | # Modules to import as nested modules of the module specified in RootModule/ModuleToProcess
69 | NestedModules = @('Module-PC_3-2-0-1.psm1')
70 |
71 | # Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export.
72 | FunctionsToExport = '*'
73 |
74 | # Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export.
75 | CmdletsToExport = '*'
76 |
77 | # Variables to export from this module
78 | VariablesToExport = '*'
79 |
80 | # Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export.
81 | AliasesToExport = '*'
82 |
83 | # DSC resources to export from this module
84 | # DscResourcesToExport = @()
85 |
86 | # List of all modules packaged with this module
87 | # ModuleList = @()
88 |
89 | # List of all files packaged with this module
90 | # FileList = @()
91 |
92 | # Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell.
93 | PrivateData = @{
94 |
95 | PSData = @{
96 |
97 | # Tags applied to this module. These help with module discovery in online galleries.
98 | # Tags = @()
99 |
100 | # A URL to the license for this module.
101 | # LicenseUri = ''
102 |
103 | # A URL to the main website for this project.
104 | # ProjectUri = ''
105 |
106 | # A URL to an icon representing this module.
107 | # IconUri = ''
108 |
109 | # ReleaseNotes of this module
110 | # ReleaseNotes = ''
111 |
112 | # Prerelease string of this module
113 | # Prerelease = ''
114 |
115 | # Flag to indicate whether the module requires explicit user acceptance for install/update/save
116 | # RequireLicenseAcceptance = $false
117 |
118 | # External dependent modules of this module
119 | # ExternalModuleDependencies = @()
120 |
121 | } # End of PSData hashtable
122 |
123 | } # End of PrivateData hashtable
124 |
125 | # HelpInfo URI of this module
126 | # HelpInfoURI = ''
127 |
128 | # Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix.
129 | # DefaultCommandPrefix = ''
130 |
131 | }
132 |
133 |
--------------------------------------------------------------------------------
/Modules/Module-PC_3-2-0-1/Module-PC_3-2-0-1.psm1:
--------------------------------------------------------------------------------
1 | <#
2 | THIS MODULE CONTAINS FUNCTIONS RELATED TO PINGCASTLE V3.2.0.1
3 |
4 | Initial Score: 65/100 (Stale: 31, Priv Accounts: 40, Trust: 00, Anomalies:65)
5 | Release Score: 05/100 (Stale: 00, Priv Accounts: 00, Trust: 00, Anomalies:05) - Will be 0 if you install a second DC
6 | Release ANSSI: Level 3
7 | Release MITRE: ATT&CK - 3 techniques matching for Credential access (DSheuristics, Auth.Users on DNS, Kerberos Armoring)
8 |
9 |
10 | Fix list:
11 | > S-OldNtlm GPO Default Domain Security
12 | > S-ADRegistration Function Resolve-SADRegistration
13 | > S-DC-SubnetMissing Function Resolve-SDCSubnetMissing
14 | > S-PwdNeverExpires Function Resolve-SPwdNeverExpires
15 | > P-Delegated Function Resolve-PDelegated
16 | > P-RecycleBin Function Resolve-PRecycleBin
17 | > P-SchemaAdmin Function Resolve-PSchemaAdmin
18 | > P-UnprotectedOU Function Resolve-PUnprotectedOU
19 | > A-LAPS-Not-Installed Function Resolve-ALAPSNotInstalled & GPO Default Domain Security Policy
20 | > A-MinPwdLen Function Resolve-AMinPwdLen
21 | > A-DC-Spooler GPO Default Domain Controller Security
22 | > A-AuditDC GPO Default Domain Controller Security
23 | > A-DC-Coerce GPO Default Domain Controller Security
24 | > A-HardenedPaths GPO Default Domain Controller Security
25 | > A-NoServicePolicy Function Resolve-SPwdNeverExpires (add the requiered PSO)
26 | > A-PreWin2000AuthenticatedUsers Function REsolve-APreWin2000AuthenticatedUsers
27 | > S-SMB-v1 GPO Default Domain Security
28 | #>
29 | #region S-ADRegistration
30 | Function Resolve-SADRegistration {
31 | <#
32 | .SYNOPSIS
33 | Resolve the alert S-ADRegistration from PingCastle.
34 |
35 | .DESCRIPTION
36 | The purpose is to ensure that basic users cannot register extra computers in the domain.
37 |
38 | .NOTES
39 | Version 01.00.00 (2024/06/09 - Creation)
40 | #>
41 |
42 | Param()
43 |
44 | # Prepare for eventlog
45 | Test-EventLog | Out-Null
46 | $LogData = @('Fixing ms-DS-MachineAccountQuota to 0:')
47 |
48 | # Fixing the value
49 | Try {
50 | Set-ADDomain -Identity (Get-ADDomain) -Replace @{"ms-DS-MachineAccountQuota" = "0" } | Out-Null
51 | $LogData += '> Successfull <'
52 | $FlagRes = 'Info'
53 | }
54 | Catch {
55 | $LogData += @('! FAILED !',' ','Error message from stack:',$Error[0].ToString())
56 | $FlagRes = 'Error'
57 | }
58 |
59 | # Checking the new value - final check
60 | if ($FlagRes -eq 'Info') {
61 | $newValue = (Get-ADObject (Get-ADDomain).distinguishedName -Properties ms-DS-MachineAccountQuota).'ms-DS-MachineAccountQuota'
62 |
63 | if ($newValue -eq 0) {
64 | $LogData += @(' ','Value checked on AD: the value is as expected.')
65 | }
66 | Else {
67 | $LogData += @(' ','Value checked on AD: the value is incorect!')
68 | $FlagRes = 'Warning'
69 | }
70 | }
71 |
72 | # Sending log and leaving with proper exit code
73 | Write-ToEventLog $FlagRes $LogData
74 | Return $FlagRes
75 | }
76 | #endregion
77 | #region S-DC-SubnetMissing
78 | Function Resolve-SDCSubnetMissing {
79 | <#
80 | .SYNOPSIS
81 | Resolve the S-DC-SubnetMissing alert from PingCastle.
82 |
83 | .DESCRIPTION
84 | Ensure that the minimum set of subnet(s) has been configured in the domain.
85 |
86 | .NOTES
87 | Version 01.00.00 (2024/06.09 - Creation)
88 | #>
89 | Param()
90 |
91 | #region INTERNAL FUNCTIONS
92 | function ConvertTo-IPv4MaskString {
93 | param(
94 | [Parameter(Mandatory = $true)]
95 | [ValidateRange(0, 32)]
96 | [Int] $MaskBits
97 | )
98 | $mask = ([Math]::Pow(2, $MaskBits) - 1) * [Math]::Pow(2, (32 - $MaskBits))
99 | $bytes = [BitConverter]::GetBytes([UInt32] $mask)
100 | (($bytes.Count - 1)..0 | ForEach-Object { [String] $bytes[$_] }) -join "."
101 | }
102 | #endregion
103 | # Init debug
104 | Test-EventLog | Out-Null
105 | $LogData = @('Fixing missing DC subnet in AD Sites:')
106 | $FlagRes = "Info"
107 |
108 | # Get the DC IP address and subnet
109 | $DCIPs = Get-NetIPAddress -AddressFamily IPv4 | Where-Object { $_.IPAddress -ne '127.0.0.1' }
110 |
111 | #region ADD SUBNET
112 | # Get IP PLAN ADDRESSES and add them to the default AD Site
113 | foreach ($DCIP in $DCIPs) {
114 | Try {
115 | $IPplan = "$(([IPAddress] (([IPAddress] "$($DCIP.IPAddress)").Address -band ([IPAddress] (ConvertTo-IPv4MaskString $DCIP.PrefixLength)).Address)).IPAddressToString)/$($DCIP.PrefixLength)"
116 | $LogData += @(" ","Checking for IP Plan: $IPplan")
117 | }
118 | Catch {
119 | $LogData += @("Checking for IP Plan: $IPplan - FATAL ERROR",' ','Error message from stack:',$Error[0].ToString())
120 | $FlagRes += "Error"
121 | }
122 |
123 | # Check if the subnet already exists
124 | Try {
125 | $findSubnet = Get-AdReplicationSubnet $IPplan -ErrorAction Stop
126 | }
127 | Catch {
128 | $findSubnet = $null
129 | }
130 |
131 | if ($findSubnet) {
132 | $LogData += "Subnet $IPplan already exists (no action)"
133 | }
134 | Else {
135 | $LogData += "Subnet $IPplan is missing."
136 | $DfltSite = (Get-AdReplicationSite).Name
137 | Try {
138 | New-AdReplicationSubnet -Site (Get-AdReplicationSite).Name -Name $IPplan -ErrorAction Stop | Out-Null
139 | $LogData += "Subnet $IPplan has been added to '$DfltSite'"
140 | }
141 | Catch {
142 | $LogData += @("Subnet $IPplan could not be added to '$DfltSite'!")
143 | $FlagRes = "Error"
144 | }
145 | }
146 | }
147 | #endregion
148 | # Sending log and leaving with proper exit code
149 | Write-ToEventLog $FlagRes $LogData
150 | Return $FlagRes
151 | }
152 | #endregion
153 | #region S-DC-SubnetMissingIPv6
154 | Function Resolve-SDCSubnetMissingIPv6 {
155 | <#
156 | .SYNOPSIS
157 | Disable IPv6 on the local system.
158 |
159 | .DESCRIPTION
160 | Check for any interface setup for IPv6 and disable IPv6 on it.
161 |
162 | .NOTES
163 | Version 01.00.00 (2024/06/15 - Creation)
164 | #>
165 | Param()
166 |
167 | # Init debug
168 | Test-EventLog | Out-Null
169 | $LogData = @('Disabling IPv6:',' ')
170 | $FlagRes = "Info"
171 |
172 | Try {
173 | Get-NetAdapterBinding -ComponentID "ms_tcpip6" | Where-Object { $_.Enabled -eq $true } | Disable-NetAdapterBinding -ComponentID "ms_tcpip6"
174 | $LogData += "ms_tcpip6 disabled on all interfaces."
175 | }
176 | Catch {
177 | $LogData += @("Failed to disable ms_tcpip6 on all interfaces.","Error: $($_.ToString())")
178 | $FlagRes = "Error"
179 | }
180 | # Sending log and leaving with proper exit code
181 | Write-ToEventLog $FlagRes $LogData
182 | Return $FlagRes
183 | }
184 | #endregion
185 | #region S-PwdNeverExpires
186 | Function Resolve-SPwdNeverExpires {
187 | <#
188 | .SYNOPSIS
189 | Resolve the S-PwdNeverExpires alert from PingCastle.
190 |
191 | .DESCRIPTION
192 | Ensure that every account has a password which is compliant with password expiration policies.
193 | To achieve this goal, some PSO will be added to the domain, including one specific to the emergency accounts.
194 |
195 | PSO List:
196 | > PSO-EmergencyAccounts-LongLive......: 5 years, complex, 30 characters, Weight is 105.
197 | > PSO-ServiceAccounts-Legacy..........: 5 years, complex, 30 characters, Weight is 105.
198 | > PSO-EmergencyAccounts-Standard......: 1 year, complex, 30 characters, Weight is 100.
199 | > PSO-Users-ChangeEvery3years.........: 3 year, complex, 16 characters, Weight is 70.
200 | > PSO-Users-ChangeEvery1year..........: 1 year, complex, 12 characters, Weight is 60.
201 | > PSO-Users-ChangeEvery3months........: 3 months, complex, 10 characters, Weight is 50.
202 | > PSO-ServiceAccounts-ExtendedLife....: 3 years, complex, 18 characters, Weight is 35.
203 | > PSO-ServiceAccounts-Standard........: 1 year, complex, 16 characters, Weight is 30.
204 | > PSO-AdminAccounts-SystemPriveleged..: 6 months, complex, 14 characters, Weight is 20.
205 | > PSO-AdminAccounts-ADdelegatedRight..: 6 months, complex, 16 characters, Weight is 15.
206 | > PSO-ServiceAccounts-ADdelegatedRight: 1 year, complex, 24 characters, Weight is 15.
207 | > PSO-AdminAccounts-ADhighPrivileges..: 6 months, complex, 20 characters, Weight is 10.
208 |
209 | To learn how thos PSO should be used in production, please have a look to the documentation (PSO Managegement.md)
210 |
211 | .NOTES
212 | Version 01.00.00 (2024/06/10 - Creation)
213 | #>
214 | Param()
215 | # Prepare logging
216 | Test-EventLog | Out-Null
217 | $LogData = @('Adding PSO to the domain:')
218 | $FlagRes = "Info"
219 |
220 | # Load XML data
221 | $psoXml = Get-XmlContent .\Configuration\DomainSettings.xml
222 |
223 | # Retrieving SID 500 SamAccountName
224 | $Sid500 = (Get-ADUser -Identity "$((Get-AdDomain).domainSID)-500").SamAccountName
225 |
226 | # Looping on PSO list
227 | foreach ($PSO in $psoXml.Settings.PwdStrategyObjects.PSO) {
228 | #region Create AD Group
229 | $LogData += " "
230 | $GrpExists = Get-ADGroup -LDAPFilter "(SAMAccountName=$($PSO.Name))"
231 | if ($GrpExists) {
232 | $LogData += "$($PSO.Name): Group already exists."
233 | }
234 | Else {
235 | Try {
236 | New-ADGroup -DisplayName $PSO.Name -Description "Group to assign the PSO: $($PSO.Name)" -GroupCategory Security -GroupScope Global -Name $PSO.Name -ErrorAction Stop | Out-Null
237 | $LogData += "$($PSO.Name): Group created successfully."
238 | }
239 | Catch {
240 | $LogData += "$($PSO.Name): Group could not be created!"
241 | $FlagRes = "Error"
242 | }
243 | }
244 | #endregion
245 | #region Checking if member is to be added
246 | if ($PSO.Member) {
247 | foreach ($Member in $PSO.Member) {
248 | if ($Member -eq 'SID-500') {
249 | $MbrSam = $Sid500
250 | }
251 | Else {
252 | $MbrSam = $Member
253 | }
254 | Try {
255 | Add-ADGroupMember -Identity $PSO.Name -Members $MbrSam -ErrorAction Stop | Out-Null
256 | $LogData += "$($PSO.Name): successfully added $MbrSam to the PSO group."
257 | }
258 | Catch {
259 | $LogData += "$($PSO.Name): failed to add $MbrSam to the PSO group!"
260 | $FlagRes = "Error"
261 | }
262 | Try {
263 | if ((Get-ADObject -Filter "SamAccountName -eq '$MbrSam'").ObjectClass -eq 'User') {
264 | Set-AdUser $MbrSam -PasswordNeverExpires 0 | Out-Null
265 | $LogData += "$($PSO.Name): User $MbrSam has been set with PasswordNeverExpires to $False"
266 | }
267 | }
268 | Catch {
269 | $LogData += "$($PSO.Name): Failed to set password expiration to $mbrSam!"
270 | }
271 | }
272 | }
273 | #endregion
274 | #region Create new PSO
275 | Try {
276 | if ((Get-ADObject -LDAPFilter "(&(name=$($PSO.Name))(ObjectClass=msDS-PasswordSettings))")) {
277 | $LogData += "$($PSO.Name): PSO already exists."
278 | }
279 | Else {
280 | new-adFineGrainedPasswordPolicy -ComplexityEnabled 1 `
281 | -Description ((($PSO.Name).Replace('PSO-','PSO for ')).Replace('-',' ')) `
282 | -DisplayName $PSO.Name `
283 | -LockOutDuration "0.0:30:0.0" `
284 | -LockoutObservationWindow "0.0:30:0.0" `
285 | -LockoutThreshold 5 `
286 | -MaxPasswordAge $PSO.MaxPwdAge `
287 | -MinPasswordAge "1.0:0:0.0" `
288 | -MinPasswordLength $PSO.PwdLength `
289 | -Name $PSO.Name `
290 | -PasswordHistoryCount 60 `
291 | -Precedence $PSO.Precedence `
292 | -ProtectedFromAccidentalDeletion 1 `
293 | -ReversibleEncryptionEnabled 0 `
294 | -OtherAttributes @{'msDS-PSOAppliesTo'=(Get-AdGroup $PSO.Name).distinguishedName} `
295 | -ErrorAction Stop | Out-Null
296 |
297 | $LogData += "$($PSO.Name): PSO successfully created."
298 | }
299 | }
300 | Catch {
301 | $LogData += "$($PSO.Name): PSO could not be created!"
302 | $FlagRes = "Error"
303 | }
304 | #endregion
305 | }
306 | # Sending log and leaving with proper exit code
307 | Write-ToEventLog $FlagRes $LogData
308 | Return $FlagRes
309 | }
310 | #endregion
311 | #region P-Delegated
312 | Function Resolve-PDelegated {
313 | <#
314 | .SYNOPSIS
315 | Reolve the P-Delegated alert from PingCastle.
316 |
317 | .DESCRIPTION
318 | Ensure that all Administrator Accounts have the configuration flag "this account is sensitive and cannot be delegated" (or are members of the built-in group "Protected Users" when your domain functional level is at least Windows Server 2012 R2).
319 |
320 | .NOTES
321 | Version 01.00.00 (2024/06/10 - Creation)
322 | #>
323 | Param()
324 |
325 | # Prepare logging
326 | Test-EventLog | Out-Null
327 | $LogData = @('Setting "Account is sensible and cannot be delegated" to empowered users:')
328 | $FlagRes = "Info"
329 |
330 | # Getting all empowered users, except KRBTGT
331 | Try {
332 | $myAdmin = "$((Get-AdDomain).DomainSID)-500"
333 | Set-AdUser $myAdmin -AccountNotDelegated 1 -ErrorAction Stop | Out-Null
334 | $LogData += "$($MyAdmin.Name): successfully set AccountNotDelegated to 1"
335 | }
336 | Catch {
337 | $LogData += @("$($MyAdmin.Name): failed tp set AccountNotDelegated to 1!",' ',"Error: $($_.ToString())")
338 | $FlagRes = "Error"
339 | }
340 |
341 | # Sending log and leaving with proper exit code
342 | Write-ToEventLog $FlagRes $LogData
343 | Return $FlagRes
344 | }
345 | #endregion
346 | #region P-RecycleBin
347 | Function Resolve-PRecycleBin {
348 | <#
349 | .SYNOPSIS
350 | Resolve the alert P-RecycleBin from PingCastle.
351 |
352 | .DESCRIPTION
353 | Ensure that the Recycle Bin feature is enabled.
354 |
355 | .NOTES
356 | Version 01.00.00 (2024/06/10 - Creation)
357 | #>
358 | Param()
359 |
360 | # Prepare logging
361 | Test-EventLog | Out-Null
362 | $LogData = @('Enabling AD RecycleBin (if needed)')
363 | $FlagRes = "Info"
364 |
365 | # Load XML data
366 | $RunSetup = Get-XmlContent .\Configuration\RunSetup.xml
367 |
368 | # Check if Recycle Bin was to enable
369 | $installRB = $RunSetup.Configuration.Forest.RecycleBin
370 |
371 | # If tasked to be installed in the forest, then doing precheck and enabling.
372 | if ($installRB -eq 'Yes') {
373 | # Am I in a child domain? If so, I don't care about RB.
374 | if ($RunSetup.Configuration.Domain.Type -eq 'Root') {
375 | # We also need to ensure that the forest level is at least 2008 R2
376 | if ([int]$RunSetup.Configuration.Forest.FunctionalLevel -ge 4) {
377 | # So far, so good... Let's enable it.
378 | Try {
379 | if ((Get-ADOptionalFeature -Filter 'name -like "Recycle Bin Feature"').EnabledScopes) {
380 | $LogData += "The AD REcycle Bin is already enabled."
381 | }
382 | Else {
383 | Enable-ADOptionalFeature 'Recycle Bin Feature' -Scope ForestOrConfigurationSet -Target (Get-ADForest).Name -WarningAction SilentlyContinue -Confirm:$false | Out-Null
384 | $LogData += "The AD Recycle Bin is now enabled."
385 | }
386 | }
387 | Catch {
388 | $LogData += "Failed to enable the AD Recycle Bin!"
389 | $FlagRes = "Error"
390 | }
391 | }
392 | Else {
393 | $LogData += "The Forest Functional Level is lower than 4 (2008R2): no Recycle Bin activation could be performed."
394 | $FlagRes = "Warning"
395 | }
396 | }
397 | Else {
398 | $LogData += "This is a $($RunSetup.Configuration.Domain.Type) domain: no Recycle Bin activation needed (forest level)."
399 | $FlagRes = "Warning"
400 | }
401 | }
402 |
403 | # Sending log and leaving with proper exit code
404 | Write-ToEventLog $FlagRes $LogData
405 | Return $FlagRes
406 | }
407 | #endregion
408 | #region P-SchemaAdmin
409 | Function Resolve-PSchemaAdmin {
410 | <#
411 | .SYNOPSIS
412 | Resolve the alert P-SchemaAdmin from PingCastle.
413 |
414 | .DESCRIPTION
415 | Ensure that no account can make unexpected modifications to the schema.
416 |
417 | .NOTES
418 | Version 01.00.00 (2024/06/10 - Creation)
419 | #>
420 | Param()
421 |
422 | # Prepare logging
423 | Test-EventLog | Out-Null
424 | $LogData = @('Dropping any account from the Schema Admins group')
425 | $FlagRes = "Info"
426 |
427 | # Remove all members
428 | Try {
429 | $Members = Get-AdGroupMember "$((Get-AdDomain).DomainSID)-518"
430 | if ($null -ne $Members) {
431 | remove-adGroupMember -Identity "$((Get-AdDomain).DomainSID)-518" -Members $Members -ErrorAction Stop -Confirm:$false | Out-Null
432 | $LogData += "Successfully removed all members from Schema Admins group."
433 | }
434 | }
435 | Catch {
436 | $LogData += "Failed to remove all members from Schema Admins group!"
437 | $FlagRes = "Error"
438 | }
439 |
440 | # Sending log and leaving with proper exit code
441 | Write-ToEventLog $FlagRes $LogData
442 | Return $FlagRes
443 | }
444 | #endregion
445 | #region P-UnprotectedOU
446 | Function Resolve-PUnprotectedOU {
447 | <#
448 | .SYNOPSIS
449 | Resolve the alert P-UnprotectedOU from PingCastle.
450 |
451 | .DESCRIPTION
452 | Ensure that Organizational Units (OUs) and Containers in Active Directory are protected to prevent accidental deletion, which could lead to data loss and disruptions in the network infrastructure.
453 |
454 | .NOTES
455 | Version 01.00.00 (2024/06/10 - Creation)
456 | #>
457 | Param()
458 |
459 | # Prepare logging
460 | Test-EventLog | Out-Null
461 | $LogData = @('Securing Organizational Units against accidental deletion:')
462 | $FlagRes = "Info"
463 |
464 | # Find OU without the option "protected against accidental deletion"
465 | $UnprotectedOU = Get-ADOrganizationalUnit -filter {name -like "*"} -Properties ProtectedFromAccidentalDeletion
466 |
467 | # Looping around...
468 | foreach ($OU in $UnprotectedOU) {
469 | $LogData += " "
470 | Try {
471 | Set-ADOrganizationalUnit -ProtectedFromAccidentalDeletion $true -Identity $OU.DistinguishedName -ErrorAction Stop | Out-Null
472 | $LogData += "$($OU.DistinguishedName): successfully protected against accidental deletion."
473 | }
474 | Catch {
475 | $LogData += "$($OU.DistinguishedName): failed to protect against accidental deletion!"
476 | }
477 | }
478 |
479 | # Sending log and leaving with proper exit code
480 | Write-ToEventLog $FlagRes $LogData
481 | Return $FlagRes
482 | }
483 | #endregion
484 | #region A-MinPwdLen
485 | Function Resolve-AMinPwdLen {
486 | <#
487 | .SYNOPSIS
488 | Resolve the alert A-MinPwdLen from PingCastle.
489 |
490 | .DESCRIPTION
491 | Verify if the password policy of the domain enforces users to have at least 8 characters in their password
492 |
493 | .NOTES
494 | Version 01.00.00 (2024/06/10 - Creation)
495 | #>
496 | Param()
497 |
498 | # Prepare logging
499 | Test-EventLog | Out-Null
500 | $LogData = @('Modifying default password strategy to x char (based on domainSettings.xml):')
501 | $FlagRes = "Info"
502 |
503 | # Load XML
504 | $DomainSettings = Get-XmlContent .\configuration\DomainSettings.xml
505 |
506 | # Get default value (if less than 8, then forced to 8)
507 | if ([int]$DomainSettings.Settings.DefaultPwdStrategy.PwdLength -ge 8) {
508 | $newLen = [int]$DomainSettings.Settings.DefaultPwdStrategy.PwdLength
509 | }
510 | Else {
511 | $newLen = 8
512 | }
513 | $LogData += "New Default Domain Policy password length value: $newLen"
514 |
515 | # Update the policy: Windows Server 2022
516 | Try {
517 | $FlagIs2k22 = $False
518 | if ((gwmi Win32_OperatingSystem).Caption -match "2022") {
519 | $FlagIs2k22 = $true
520 | Set-ADDefaultDomainPasswordPolicy -ComplexityEnabled 1 -Confirm:$false -Identity (Get-ADDomain).DistinguishedName `
521 | -LockOutDuration 0.0:15:0.0 -LockoutObservationWindow 0.0:5:0.0 -LockoutThreshold 5 `
522 | -MaxPasswordAge 365.0:0:0.0 -MinPasswordAge 1.0:0:0.0 -MinPasswordLength $newLen `
523 | -PasswordHistoryCount 24 -ReversibleEncryptionEnabled 0
524 |
525 | $LogData += @("Using Set-ADDefaultDomainPasswordPolicy:",' ',"Complexity: Enabled", "Lockout duration: 15 min.", "Lockout observation: 5 min.", "Lockout threshold: 5", "Max pwd age: 365 days", "Min pwd age: 1 day", "Password Min Length: $newLen", "Password History: 24", "Reversible encryption: False")
526 | }
527 | Else {
528 | $LogData += "Sorry, this function does not handle OS release beneath Windows Server 2022. Will try to set this up by manipulating secpol..."
529 | }
530 | }
531 | Catch {
532 | $LogData += @("Failed to update the default password strategy for your domain!","Error: $($_.ToString())")
533 | $FlagRes = "Error"
534 | }
535 |
536 | # Update the policy: Windows Server 2019 or younger
537 | Try {
538 | if (-not($FlagIs2k22)) {
539 | [void](secedit /export /cfg .\secpol.cfg)
540 | [void]((Get-Content .\secpol.cfg).replace("MaximumPasswordAge = 42", "MaximumPasswordAge = 365") | Out-File .\secpol.cfg)
541 | [void]((Get-Content .\secpol.cfg).replace("MinimumPasswordLength = 7", "MinimumPasswordLength = $NewLen") | Out-File .\secpol.cfg)
542 | [void](secedit /configure /db c:\windows\security\local.sdb /cfg .\secpol.cfg /areas SECURITYPOLICY)
543 | [void](rm -force .\secpol.cfg -confirm:$false)
544 |
545 | $LogData += @("Using SECPOL:",' ',"Complexity: Enabled", "Lockout duration: N/A", "Lockout observation: N/A", "Lockout threshold: 0", "Max pwd age: 365 days", "Min pwd age: 1 day", "Password Min Length: $newLen", "Password History: 24", "Reversible encryption: False")
546 | }
547 | Else {
548 | if (-not($FlagIs2k22)) {
549 | $LogData += "Failed to identify how to setup the default password strategy!"
550 | $FlagRes = "Warning"
551 | }
552 | }
553 | }
554 | Catch {
555 | $LogData += @("Failed to update the default password strategy for your domain!","Error: $($_.ToString())")
556 | $FlagRes = "Error"
557 | }
558 |
559 | # Sending log and leaving with proper exit code
560 | Write-ToEventLog $FlagRes $LogData
561 | Return $FlagRes
562 | }
563 | #endregion
564 | #region A-PreWin2000AuthenticatedUsers
565 | Function Resolve-APreWin2000AuthenticatedUsers {
566 | <#
567 | .SYNOPSIS
568 | Resolve the alert A-PreWin2000AuthenticatedUsers from PingCastle.
569 |
570 | .DESCRIPTION
571 | Ensure that the "Pre-Windows 2000 Compatible Access" group does not contains "Authenticated Users".
572 |
573 | .NOTES
574 | Version 01.00.00 (2024/06/10 - Creation)
575 | #>
576 | Param()
577 |
578 | # Prepare logging
579 | Test-EventLog | Out-Null
580 | $LogData = @("Dropping content from 'Pre-Windows 2000 Compatible Access':")
581 | $FlagRes = "Info"
582 |
583 | # Flubbing the group
584 | Try {
585 | Set-adGroup -Identity "S-1-5-32-554" -Clear member
586 | $LogData += "Group S-1-5-32-554 flushed."
587 | }
588 | Catch {
589 | $LogData += "Group S-1-5-32-554 could not be flushed!"
590 | $FlagRes = "Error"
591 | }
592 |
593 | # Sending log and leaving with proper exit code
594 | Write-ToEventLog $FlagRes $LogData
595 | Return $FlagRes
596 | }
597 | #endregion
598 | #region A-LAPS-NOT-Installed
599 | Function Resolve-ALAPSNotInstalled {
600 | <#
601 | .SYNOPSIS
602 | This function resolve the alert A-LAPS-NOT-Installed from PingCastle.
603 |
604 | .DESCRIPTION
605 | Ensure that LAPS is in place for the whole domain. If the DFL and/or OS.Caption does not meet minimum requierement for Windows LAPS,
606 | the script will the deploy the MS LAPS binaries (legacy mode).
607 |
608 | .EXTERNALHELP
609 | https://learn.microsoft.com/fr-fr/windows-server/identity/laps/laps-scenarios-windows-server-active-directory
610 |
611 | .NOTES
612 | Version 01.00.00 (2024/06/12 - Creation)
613 | #>
614 | Param()
615 |
616 | # Logs
617 | Test-EventLog | Out-Null
618 | $LogData = @('Enable LAPS on your domain:', ' ')
619 | $FlagRes = "Info"
620 |
621 | # Get DC OS Caption and DFL.
622 | $OSCaption = (gwmi Win32_OperatingSystem).Caption
623 | $DomainDFL = (Get-AdDomain).DomainMode
624 | $OSArchitr = (gwmi Win32_OperatingSystem).OSArchitecture
625 |
626 | $LogData += @("OS Caption: $($OSCaption)","Domain Functional Level: $($DomainDFL)","OS Architecture: $($OSArchitr)",' ')
627 |
628 | # Updating Schema, if possible
629 | if (($OSCaption -match '2019' -or $OSCaption -match '2022') -and ($DomainDFL -match '2016')) {
630 | $LogData += @("The prerequesite to Windows LAPS are fullfilled."," ")
631 | Try {
632 | [void](Update-LapsADSchema -Confirm:$false -ErrorAction Stop)
633 | $LogData += @("AD Schema extended with Windows LAPS.","Beware: the extension does not implies automatic activation.","Please ensure the GPO Default Domain Security is propperly setup.",' ')
634 | }
635 | Catch {
636 | $FlagRes = "Error"
637 | $LogData += @("Failed to extend the schema!","Error: $($_.ToString())",' ')
638 | }
639 | }
640 | Else {
641 | $LogData += @("The prerequesite to Windows LAPS are not fullfilled (OS not 2019+ and/or DFL not 2016)"," ")
642 | Try {
643 | if ($OSArchitr -match '64') {
644 | [void](Start-Process -FilePath "$env:systemroot\system32\msiexec.exe" -WorkingDirectory ".\IMPORTS\MS LAPS" -ArgumentList '/i laps.x64.msi ADDLOCAL=Management.UI,Management.PS,Management.ADMX /quiet /norestart' -NoNewWindow -Wait -ErrorAction Stop)
645 | }
646 | Else {
647 | [void](Start-Process -FilePath "$env:systemroot\system32\msiexec.exe" -WorkingDirectory ".\IMPORTS\MS LAPS" -ArgumentList '/i laps.x86.msi ADDLOCAL=Management.UI,Management.PS,Management.ADMX /quiet /norestart' -NoNewWindow -Wait -ErrorAction Stop)
648 | }
649 | $LogData += @("MS LAPS installed.",' ')
650 | }
651 | Catch {
652 | $LogData += @("Failed to install MS LAPS binaries!","Error: $($_.ToString())"," ")
653 | $FlagRes = "Error"
654 | }
655 | if ($FlagRes -eq 'Info') {
656 | # Extending Schema
657 | Try {
658 | [void](Import-Module AdmPwd.PS -ErrorAction Stop -WarningAction Stop)
659 | [void](Update-AdmPwdADSchema -ErrorAction Stop)
660 | $LogData += @("Schema updated for MS LAPS successfully.",' ')
661 | }
662 | Catch {
663 | $LogData += @("Failed to update the Schema for MS LAPS!","Error: $($_.ToString())",' ')
664 | $FlagRes = "Error"
665 | }
666 | }
667 | }
668 |
669 | $LogData += 'Please note that this script do not enable any permission - you still have to setup LAPS to match your needs.'
670 | # Sending log and leaving with proper exit code
671 | Write-ToEventLog $FlagRes $LogData
672 | Return $FlagRes
673 | }
674 | #endregion
--------------------------------------------------------------------------------
/Modules/Module-PK_4-2/Module-PK_4-2.psd1:
--------------------------------------------------------------------------------
1 | #
2 | # Module manifest for module 'Module-PK_4-2'
3 | #
4 | # Generated by: loic.veirman@mssec.fr
5 | #
6 | # Generated on: 16/06/2024
7 | #
8 |
9 | @{
10 |
11 | # Script module or binary module file associated with this manifest.
12 | RootModule = 'Module-PK_4-2.psm1'
13 |
14 | # Version number of this module.
15 | ModuleVersion = '1.0.0.0'
16 |
17 | # Supported PSEditions
18 | # CompatiblePSEditions = @()
19 |
20 | # ID used to uniquely identify this module
21 | GUID = '3fcfa599-a6f0-4bd4-bd9b-d32c04587efb'
22 |
23 | # Author of this module
24 | Author = 'loic.veirman@mssec.fr'
25 |
26 | # Company or vendor of this module
27 | CompanyName = 'Unknown'
28 |
29 | # Copyright statement for this module
30 | Copyright = '(c) loic.veirman@mssec.fr. All rights reserved.'
31 |
32 | # Description of the functionality provided by this module
33 | # Description = ''
34 |
35 | # Minimum version of the PowerShell engine required by this module
36 | # PowerShellVersion = ''
37 |
38 | # Name of the PowerShell host required by this module
39 | # PowerShellHostName = ''
40 |
41 | # Minimum version of the PowerShell host required by this module
42 | # PowerShellHostVersion = ''
43 |
44 | # Minimum version of Microsoft .NET Framework required by this module. This prerequisite is valid for the PowerShell Desktop edition only.
45 | # DotNetFrameworkVersion = ''
46 |
47 | # Minimum version of the common language runtime (CLR) required by this module. This prerequisite is valid for the PowerShell Desktop edition only.
48 | # ClrVersion = ''
49 |
50 | # Processor architecture (None, X86, Amd64) required by this module
51 | # ProcessorArchitecture = ''
52 |
53 | # Modules that must be imported into the global environment prior to importing this module
54 | # RequiredModules = @()
55 |
56 | # Assemblies that must be loaded prior to importing this module
57 | # RequiredAssemblies = @()
58 |
59 | # Script files (.ps1) that are run in the caller's environment prior to importing this module.
60 | # ScriptsToProcess = @()
61 |
62 | # Type files (.ps1xml) to be loaded when importing this module
63 | # TypesToProcess = @()
64 |
65 | # Format files (.ps1xml) to be loaded when importing this module
66 | # FormatsToProcess = @()
67 |
68 | # Modules to import as nested modules of the module specified in RootModule/ModuleToProcess
69 | NestedModules = @('Module-PK_4-2.psm1')
70 |
71 | # Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export.
72 | FunctionsToExport = '*'
73 |
74 | # Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export.
75 | CmdletsToExport = '*'
76 |
77 | # Variables to export from this module
78 | VariablesToExport = '*'
79 |
80 | # Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export.
81 | AliasesToExport = '*'
82 |
83 | # DSC resources to export from this module
84 | # DscResourcesToExport = @()
85 |
86 | # List of all modules packaged with this module
87 | # ModuleList = @()
88 |
89 | # List of all files packaged with this module
90 | # FileList = @()
91 |
92 | # Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell.
93 | PrivateData = @{
94 |
95 | PSData = @{
96 |
97 | # Tags applied to this module. These help with module discovery in online galleries.
98 | # Tags = @()
99 |
100 | # A URL to the license for this module.
101 | # LicenseUri = ''
102 |
103 | # A URL to the main website for this project.
104 | # ProjectUri = ''
105 |
106 | # A URL to an icon representing this module.
107 | # IconUri = ''
108 |
109 | # ReleaseNotes of this module
110 | # ReleaseNotes = ''
111 |
112 | # Prerelease string of this module
113 | # Prerelease = ''
114 |
115 | # Flag to indicate whether the module requires explicit user acceptance for install/update/save
116 | # RequireLicenseAcceptance = $false
117 |
118 | # External dependent modules of this module
119 | # ExternalModuleDependencies = @()
120 |
121 | } # End of PSData hashtable
122 |
123 | } # End of PrivateData hashtable
124 |
125 | # HelpInfo URI of this module
126 | # HelpInfoURI = ''
127 |
128 | # Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix.
129 | # DefaultCommandPrefix = ''
130 |
131 | }
132 |
133 |
--------------------------------------------------------------------------------
/Modules/Module-PK_4-2/Module-PK_4-2.psm1:
--------------------------------------------------------------------------------
1 | <#
2 | THIS MODULE CONTAINS FUNCTIONS RELATED TO PURPLE KNIGHT V4.2
3 |
4 | Initial Score: 98/100 (AD DElegation: 100, Account Security: 99, AD Infrastructure Security: 99, Group Policy Security: 100, Kerberos Security: 99, Hybrid: N/A)
5 | Release Score: 100/100 (AD DElegation: 100, Account Security: 100, AD Infrastructure Security: 100, Group Policy Security: 100, Kerberos Security: 100, Hybrid: N/A)
6 |
7 | Disabled Test:
8 | > Permission changes on AdminSDHolder object Alert expected: the domain has just been built.
9 | > gMSA not in use Alert expected: there is no need of gMSA at this stage.
10 | > Built-in domain Administrator account used within the last two weeks Alert expected: this is the only account.
11 | > Recent privileged account creation activity Alert expected: the domain has just been built.
12 | > Changes to privileged group membership in the last 7 days Alert expected: the domain has just been built.
13 | > Changes to Default Domain Policy or Default Domain Controllers Policy in the last 7 days Alert expected: the domain has just been built.
14 |
15 | Fix list:
16 | > LDAP signing is not required on Domain Controllers Function Resolve-LDAPSrequired & GPO Default Domain Security & GPO Default Domain Controllers Security
17 | > RC4 or DES encryption type are supported by Domain Controllers GPO Default Domain Controllers Security
18 | > Protected Users group not in use Function Resolve-ProtectedUsers
19 | #>
20 | #region PK LDAPS REQUIERED
21 | Function Resolve-LDAPSrequired {
22 | <#
23 | .SYNOPSIS
24 | Create a self-signed certificate on DC for LDAPS purpose.
25 |
26 | .DESCRIPTION
27 | As the domain is a fresh built one, there is no pki or whatever. A self-signed certificate will allow this DC to use LDAPS.
28 |
29 | .NOTES
30 | Version 01.00.00 (2024/06/16 - Creation)
31 | #>
32 | Param()
33 |
34 | # Prepare logging
35 | Test-EventLog | Out-Null
36 | $LogData = @('Create a Self-Signed certificate for LDAPS:',' ')
37 | $FlagRes = "Info"
38 |
39 | # Retrieve DC data
40 | $DCfullname = "$($env:ComputerName).$($env:UserDNSdomain)"
41 | $DCname = $env:ComputerName
42 | $DomainName = $env:userdnsdomain
43 | $LogData += @("DNS Name will be $DCfullname and $DomainName.","Certificate name will be $DCname.",' ')
44 |
45 | # Generate New Cert
46 | Try {
47 | $myCert = New-SelfSignedCertificate -DnsName $DCfullname, $DCname, $DomainName -CertStoreLocation cert:\LocalMachine\My -ErrorAction Stop
48 | $LogData += @('Certificate successfully generated.',"Command: New-SelfSignedCertificate -DnsName $DCfullname, $DCname -CertStoreLocation cert:\LocalMachine\My -ErrorAction Stop",' ')
49 | }
50 | Catch {
51 | $FlagRes = "Error"
52 | $LogData += @("Failed to generate the certificate!","Error: $($_.ToString())")
53 | }
54 |
55 | # Moving cert to Trusted Root
56 | Try {
57 | $CertStore = New-Object System.Security.Cryptography.X509Certificates.X509Store -ArgumentList 'Root','LocalMachine'
58 | $CertStore.Open('ReadWrite')
59 |
60 | $LogData += @('CertStore LocalMachine\Root open in read/write successfully.',' ')
61 |
62 | $Certificate = Get-ChildItem -Path "Cert:\LocalMachine\My" -Recurse | Where-Object { $_.thumbprint -eq $myCert.Thumbprint }
63 | if ($Certificate) {
64 | $LogData += @("Certificate $DCName found in LocalMachine\My.","Thumbprint: $($Certificate.Thumbprint)")
65 |
66 | [void]$CertStore.Add($Certificate)
67 | [void]$CertStore.Close()
68 |
69 | $LogData += @("Certificate $DCName copied to LocalMachine\Root.",' ')
70 | }
71 | Else {
72 | $LogData += @("Certificate $DCName not found in LocalMachine\My!","Error: $($_.ToString())")
73 | $FlagRes = "Error"
74 | }
75 | }
76 | Catch {
77 | $LogData += @("Certificate $DCName failed to be copied in LocalMachine\Root!","Error: $($_.ToString())")
78 | $FlagRes = "Error"
79 | }
80 | # Sending log and leaving with proper exit code
81 | Write-ToEventLog $FlagRes $LogData
82 | Return $FlagRes
83 | }
84 | #endregion
85 |
86 | #region PK Protected Users
87 | Function Resolve-ProtectedUsers {
88 | <#
89 | .SYNOPSIS
90 | Function to fix the alert "protected users not in use" from Purple Knight.
91 |
92 | .DESCRIPTION
93 | This function add the administrator account to the protected users group.
94 |
95 | .NOTES
96 | Version 01.00.00 (2024/06/16 - Creation)
97 | #>
98 | Param()
99 |
100 | # Prepare logging
101 | Test-EventLog | Out-Null
102 | $LogData = @('Add Administrator to Protected Users:',' ')
103 | $FlagRes = "Info"
104 |
105 | # Adding administrator to PUG.
106 | Try {
107 | [void](Add-AdGroupMember -identity "Protected Users" -Members (Get-AdUser "$((Get-AdDomain).DomainSID)-500") -ErrorAction Stop)
108 | $LogData += "Account successfully added."
109 | }
110 | Catch {
111 | $LogData += @("Failed to add the account to the group!"," ","Error: $($_.ToString())")
112 | $FlagRes = "Error"
113 | }
114 |
115 | # Sending log and leaving with proper exit code
116 | Write-ToEventLog $FlagRes $LogData
117 | Return $FlagRes
118 |
119 | }
120 | #endregion
--------------------------------------------------------------------------------
/Modules/Module-Passwords/Module-Passwords.psd1:
--------------------------------------------------------------------------------
1 | #
2 | # Module manifest for module 'Module-Logs'
3 | #
4 | # Generated by: Loic VEIRMAN (MSSec)
5 | #
6 | # Generated on: 10/05/2024
7 | #
8 |
9 | @{
10 |
11 | # Script module or binary module file associated with this manifest.
12 | RootModule = 'Module-Passwords.psm1'
13 |
14 | # Version number of this module.
15 | ModuleVersion = '1.0.0.0'
16 |
17 | # Supported PSEditions
18 | # CompatiblePSEditions = @()
19 |
20 | # ID used to uniquely identify this module
21 | GUID = '467e7361-e197-43e7-a49b-b8af2614346f'
22 |
23 | # Author of this module
24 | Author = 'Loic VEIRMAN (MSSec)'
25 |
26 | # Company or vendor of this module
27 | CompanyName = 'MSSec'
28 |
29 | # Copyright statement for this module
30 | Copyright = '(c) Loic VEIRMAN (MSSec). All rights reserved.'
31 |
32 | # Description of the functionality provided by this module
33 | Description = 'This module is intended to manage password creation and storage.'
34 |
35 | # Minimum version of the PowerShell engine required by this module
36 | PowerShellVersion = '5.1'
37 |
38 | # Name of the PowerShell host required by this module
39 | # PowerShellHostName = ''
40 |
41 | # Minimum version of the PowerShell host required by this module
42 | # PowerShellHostVersion = ''
43 |
44 | # Minimum version of Microsoft .NET Framework required by this module. This prerequisite is valid for the PowerShell Desktop edition only.
45 | # DotNetFrameworkVersion = ''
46 |
47 | # Minimum version of the common language runtime (CLR) required by this module. This prerequisite is valid for the PowerShell Desktop edition only.
48 | ClrVersion = '4.0'
49 |
50 | # Processor architecture (None, X86, Amd64) required by this module
51 | # ProcessorArchitecture = ''
52 |
53 | # Modules that must be imported into the global environment prior to importing this module
54 | # RequiredModules = @("Module-Logs.psm1")
55 |
56 | # Assemblies that must be loaded prior to importing this module
57 | # RequiredAssemblies = @()
58 |
59 | # Script files (.ps1) that are run in the caller's environment prior to importing this module.
60 | # ScriptsToProcess = @()
61 |
62 | # Type files (.ps1xml) to be loaded when importing this module
63 | # TypesToProcess = @()
64 |
65 | # Format files (.ps1xml) to be loaded when importing this module
66 | # FormatsToProcess = @()
67 |
68 | # Modules to import as nested modules of the module specified in RootModule/ModuleToProcess
69 | # NestedModules = @('Module-Logs')
70 |
71 | # Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export.
72 | FunctionsToExport = '*'
73 |
74 | # Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export.
75 | CmdletsToExport = '*'
76 |
77 | # Variables to export from this module
78 | # VariablesToExport = @()
79 |
80 | # Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export.
81 | # AliasesToExport = @()
82 |
83 | # DSC resources to export from this module
84 | # DscResourcesToExport = @()
85 |
86 | # List of all modules packaged with this module
87 | # ModuleList = @()
88 |
89 | # List of all files packaged with this module
90 | # FileList = @()
91 |
92 | # Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell.
93 | PrivateData = @{
94 |
95 | PSData = @{
96 |
97 | # Tags applied to this module. These help with module discovery in online galleries.
98 | # Tags = @()
99 |
100 | # A URL to the license for this module.
101 | # LicenseUri = ''
102 |
103 | # A URL to the main website for this project.
104 | # ProjectUri = ''
105 |
106 | # A URL to an icon representing this module.
107 | # IconUri = ''
108 |
109 | # ReleaseNotes of this module
110 | ReleaseNotes = 'Module Creation'
111 |
112 | # Prerelease string of this module
113 | # Prerelease = ''
114 |
115 | # Flag to indicate whether the module requires explicit user acceptance for install/update/save
116 | # RequireLicenseAcceptance = $false
117 |
118 | # External dependent modules of this module
119 | # ExternalModuleDependencies = @()
120 |
121 | } # End of PSData hashtable
122 |
123 | } # End of PrivateData hashtable
124 |
125 | # HelpInfo URI of this module
126 | # HelpInfoURI = ''
127 |
128 | # Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix.
129 | # DefaultCommandPrefix = ''
130 | }
131 |
132 |
--------------------------------------------------------------------------------
/Modules/Module-Passwords/Module-Passwords.psm1:
--------------------------------------------------------------------------------
1 | <#
2 | THIS MODULE CONTAINS FUNCTIONS RELATED TO PASSWORD MANAGEMENT
3 | #>
4 | Function New-RandomComplexPasword {
5 | <#
6 | .SYNOPSIS
7 | Generate a random and complex password.
8 |
9 | .DESCRIPTION
10 | Generate a complex password long as specified by the Length parameter. If AsClearText is used, then the password is returned in clear text. Else it will be cyphered.
11 |
12 | .PARAMETER Length
13 | Length size of the password. When not used, the password length will be fixed to 12.
14 |
15 | .PARAMETER AsClearText
16 | When used, ask the function to not cypher the password.
17 |
18 | .NOTES
19 | Version: 01.000.000 -- Loic VEIRMAN (MSSec)
20 | History: 2024/05/10 -- Script creation.
21 | #>
22 |
23 | Param(
24 | # Password Length
25 | [Parameter(mandatory=$False)]
26 | [int]
27 | $Length,
28 |
29 | # Force clear text
30 | [Parameter(Mandatory=$false)]
31 | [Switch]
32 | $AsClearText
33 | )
34 |
35 | # Init log
36 | Test-EventLog | Out-Null
37 | $DbgLog = @()
38 | $DbgLog = @("GENERATE NEW COMPLEX RANDOM PASSWORD","-----------","Parameter Length: $Length","Parameter AsClearText: $AsClearText"," ")
39 |
40 | # Ensure a password length is set.
41 | if (-not($Length)) {
42 | # Enforce password length to 12
43 | [int]$Length = 12
44 | $DbgLog += "Password Length enforced to 12 characters (default value)."
45 | }
46 |
47 | # Generate password
48 | $randomMiddle = [int]($Length / 2)
49 |
50 | $minSpecial = 1
51 | $minDigits = 1
52 | $minChars = 1
53 |
54 | $Special = '"`''#%&,:;<>=@{}~$()*+/\?[]^|'
55 | $Digits = '0123456789'
56 | $Chars = 'AZERTYUIOPQSDFGHJKLMWXCVBNazertyuiopqsdfghjklmwxcvbn'
57 |
58 | $QuotaSpecial = $minSpecial
59 | $QuotaDigits = $minDigits
60 | $QuotaChars = $minChars
61 |
62 | $zPassword = $null
63 |
64 | $DbgLog += @("Default settings: at least $($minSpecial *2) special char, $($minDigits * 2) digits and $($minChars * 2) letters.",' ')
65 |
66 | for ($i = 1 ; $i -le $Length ; $i++) {
67 | # Build allowed char list for this round
68 | $characters = ""
69 | if ($QuotaSpecial -gt 0) {
70 | $characters += $Special
71 | }
72 | if ($QuotaDigits -gt 0) {
73 | $characters += $Digits
74 | }
75 | if ($QuotaChars -gt 0) {
76 | $characters += $Chars
77 | }
78 | if ($QuotaSpecial -le 0 -and $QuotaDigits -le 0 -and $QuotaChars -le 0) {
79 | $characters += "$Special$Digits$Chars"
80 | }
81 |
82 | # Convert to char array
83 | $characters = $characters.ToCharArray()
84 |
85 | # randomize the character for this round
86 | $randomChar = $characters | Get-Random -Count 1
87 |
88 | # decrement counter for the specific char type
89 | if (Compare-Object $Special.ToCharArray() $randomChar -IncludeEqual -ExcludeDifferent) {
90 | $QuotaSpecial--
91 | }
92 | if (Compare-Object $Digits.ToCharArray() $randomChar -IncludeEqual -ExcludeDifferent) {
93 | $QuotaDigits--
94 | }
95 | if (Compare-Object $Chars.ToCharArray() $randomChar -IncludeEqual -ExcludeDifferent) {
96 | $QuotaChars--
97 | }
98 | # If we have reach the middle of password length, we do reinit quota to initial values
99 | if ($i -eq $randomMiddle) {
100 | $QuotaSpecial = $minSpecial
101 | $QuotaDigits = $minDigits
102 | $QuotaChars = $minChars
103 | $DbgLog += @("round $($i): Quotas reinitialized."," ")
104 | }
105 |
106 | # Adding character to password
107 | $zPassword += $randomChar
108 | $randomChar = $null
109 | }
110 |
111 | # Cyphering the password before sending it
112 | if (-not ($AsClearText)) {
113 | $yourPassword = ConvertTo-SecureString -AsPlainText $zPassword -Force
114 | $DbgLog += "final: password converted to secure string."
115 | }
116 | Else {
117 | $yourPassword = $zPassword
118 | $DbgLog += "final: password kept as clear text."
119 | }
120 |
121 | # Export to log
122 | Write-ToEventLog INFO $DbgLog | Out-Null
123 |
124 | # Return password
125 | return $yourPassword
126 | }
127 |
128 | Function New-LurchPassphrase {
129 | <#
130 | .SYNOPSIS
131 | Return a passphrase as password.
132 |
133 | .DESCRIPTION
134 | Lurch can generate grumphy password phrase to ease in write them down while being secure.
135 |
136 | .NOTES
137 | Version 01.00.00 (2024/06/26 - Creation)
138 | #>
139 |
140 | #region initialize
141 | # No log
142 | # Init lurch word database
143 | $ScriptSettings = Get-XmlContent .\Configuration\ScriptSettings.xml
144 | $LurchWordsList = [array]($ScriptSettings.Settings.Lurch.WordList -split ';')
145 | $LurchKnownWord = $LurchWordsList.Count
146 | $passphraseBomb = @('-','+','=','_',' ')
147 | #endregion
148 |
149 | #region build passphrase
150 | $words = 0
151 | $passphrase = ""
152 | While ($words -lt 5) {
153 | $Random = Get-Random -Minimum 0 -Maximum ($LurchKnownWord -1)
154 | $newWord = $LurchWordsList[$Random]
155 | if ($passphrase -ne "") {
156 | $random = Get-Random -Minimum 0 -Maximum 4
157 | $newWord = "$($passphraseBomb[$random])$newWord"
158 | }
159 | $passphrase += $newWord
160 | $words++
161 | }
162 | #endregion
163 |
164 | #Return Password
165 | return $passphrase
166 | }
167 |
168 | Export-ModuleMember -Function *
--------------------------------------------------------------------------------
/Modules/Module-Screening/Module-Screening.psd1:
--------------------------------------------------------------------------------
1 | #
2 | # Module manifest for module 'Module-Screening'
3 | #
4 | # Generated by: Loic VEIRMAN (MSSec)
5 | #
6 | # Generated on: 11/05/2024
7 | #
8 |
9 | @{
10 |
11 | # Script module or binary module file associated with this manifest.
12 | RootModule = 'Module-Screening.psm1'
13 |
14 | # Version number of this module.
15 | ModuleVersion = '1.0.0.0'
16 |
17 | # Supported PSEditions
18 | # CompatiblePSEditions = @()
19 |
20 | # ID used to uniquely identify this module
21 | GUID = 'b49bc2de-1f55-40fa-a8d8-9c9e5738168f'
22 |
23 | # Author of this module
24 | Author = 'Loic VEIRMAN (MSSec)'
25 |
26 | # Company or vendor of this module
27 | CompanyName = 'MSSec'
28 |
29 | # Copyright statement for this module
30 | Copyright = '(c) Loic VEIRMAN (MSSec). All rights reserved.'
31 |
32 | # Description of the functionality provided by this module
33 | Description = 'This module is intended to manage display output in a text console.'
34 |
35 | # Minimum version of the PowerShell engine required by this module
36 | PowerShellVersion = '5.0'
37 |
38 | # Name of the PowerShell host required by this module
39 | # PowerShellHostName = ''
40 |
41 | # Minimum version of the PowerShell host required by this module
42 | # PowerShellHostVersion = ''
43 |
44 | # Minimum version of Microsoft .NET Framework required by this module. This prerequisite is valid for the PowerShell Desktop edition only.
45 | # DotNetFrameworkVersion = ''
46 |
47 | # Minimum version of the common language runtime (CLR) required by this module. This prerequisite is valid for the PowerShell Desktop edition only.
48 | # ClrVersion = ''
49 |
50 | # Processor architecture (None, X86, Amd64) required by this module
51 | # ProcessorArchitecture = ''
52 |
53 | # Modules that must be imported into the global environment prior to importing this module
54 | # RequiredModules = @()
55 |
56 | # Assemblies that must be loaded prior to importing this module
57 | # RequiredAssemblies = @()
58 |
59 | # Script files (.ps1) that are run in the caller's environment prior to importing this module.
60 | # ScriptsToProcess = @()
61 |
62 | # Type files (.ps1xml) to be loaded when importing this module
63 | # TypesToProcess = @()
64 |
65 | # Format files (.ps1xml) to be loaded when importing this module
66 | # FormatsToProcess = @()
67 |
68 | # Modules to import as nested modules of the module specified in RootModule/ModuleToProcess
69 | NestedModules = @('Module-Screening.psm1')
70 |
71 | # Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export.
72 | FunctionsToExport = '*'
73 |
74 | # Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export.
75 | CmdletsToExport = '*'
76 |
77 | # Variables to export from this module
78 | VariablesToExport = '*'
79 |
80 | # Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export.
81 | AliasesToExport = '*'
82 |
83 | # DSC resources to export from this module
84 | # DscResourcesToExport = @()
85 |
86 | # List of all modules packaged with this module
87 | # ModuleList = @()
88 |
89 | # List of all files packaged with this module
90 | # FileList = @()
91 |
92 | # Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell.
93 | PrivateData = @{
94 |
95 | PSData = @{
96 |
97 | # Tags applied to this module. These help with module discovery in online galleries.
98 | # Tags = @()
99 |
100 | # A URL to the license for this module.
101 | # LicenseUri = ''
102 |
103 | # A URL to the main website for this project.
104 | # ProjectUri = ''
105 |
106 | # A URL to an icon representing this module.
107 | # IconUri = ''
108 |
109 | # ReleaseNotes of this module
110 | ReleaseNotes = 'Module Creation'
111 |
112 | # Prerelease string of this module
113 | # Prerelease = ''
114 |
115 | # Flag to indicate whether the module requires explicit user acceptance for install/update/save
116 | # RequireLicenseAcceptance = $false
117 |
118 | # External dependent modules of this module
119 | # ExternalModuleDependencies = @()
120 |
121 | } # End of PSData hashtable
122 |
123 | } # End of PrivateData hashtable
124 |
125 | # HelpInfo URI of this module
126 | # HelpInfoURI = ''
127 |
128 | # Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix.
129 | # DefaultCommandPrefix = ''
130 |
131 | }
132 |
133 |
--------------------------------------------------------------------------------
/Modules/Module-Screening/Module-Screening.psm1:
--------------------------------------------------------------------------------
1 | <#
2 | THIS MODULE CONTAINS FUNCTIONS TO HANDLE SCREEN OUTPUT.
3 | #>
4 |
5 | Function Format-ScreenText {
6 | <#
7 | .SYNOPSIS
8 | Split a text to multiple lines with no cutted word on screen.
9 |
10 | .DESCRIPTION
11 | When a text is displayed on screen, the text may be badly formated on screen and words may be cut, making text uneasy to read.
12 | This function return an array containing text line having a total length lower that the screen width display.
13 |
14 | .PARAMETER Text
15 | The text to be formated. Can be an array or a string.
16 |
17 | .NOTES
18 | Version: 01.000.000 -- Loic VEIRMAN (MSSec)
19 | History: 2024/05/11 -- Script creation.
20 | #>
21 | [CmdletBinding()]
22 | param (
23 | [Parameter(mandatory,Position=0)]
24 | [Array]
25 | $Text
26 | )
27 |
28 | # No logging for this function.
29 | # Getting window data
30 | $console = $host.ui.rawui
31 | $ConsoleSize = $console.WindowSize
32 | $ConsoleWidth = $ConsoleSize.Width
33 |
34 | # Fire-up result array
35 | $Result = @()
36 |
37 | foreach ($line in $Text) {
38 | # We split $line to words by using " " as spearator.
39 | $WordList = $line -split ' '
40 |
41 | # We add word up to the max width minus 2.
42 | $TmpLine = $null
43 | foreach ($word in $WordList) {
44 | # Check if, by adding a word, the TmpLine length is greater than the widh...
45 | if (($TmpLine.length + $word.length) -gt $ConsoleWidth - 2) {
46 | # Then we add the TmpLine to the result array, null it and start a new line.
47 | $Result += $TmpLine
48 | $TmpLine = $word
49 | }
50 | Else {
51 | # We add the word to TmpLine.
52 | if ($TmpLine.length -eq 0) {
53 | # Use case: first line, first word...
54 | $TmpLine = $word
55 | }
56 | Else {
57 | $TmpLine += " $($word)"
58 | }
59 | }
60 | }
61 | # End of line, we move to a new one.
62 | $Result += $TmpLine
63 | }
64 |
65 | # return result
66 | return $Result
67 | }
68 |
69 | Function New-ModuleScreeningXmlFile {
70 | <#
71 | .SYNOPSIS
72 | Will generate a default Module-Screening.xml file.
73 |
74 | .DESCRIPTION
75 | Will ensure our functions will works if the file Module-Screening.xml is badly formatted or missing.
76 |
77 | .NOTES
78 | Version: 01.000.000 -- Loic VEIRMAN (MSSec)
79 | History: 2024/05/11 -- Script creation.
80 | #>
81 | Param()
82 |
83 | # No Logging
84 | # First, dealing with a badly formated xml file.
85 | # We backup it ONLY if a backup file is not already present. Else, we delete the file.
86 | if ((Test-Path .\Modules\Module-Screening\Module-Screening.xml) -and -not ((Test-Path .\Modules\Module-Screening\Module-Screening.xml.bak))) {
87 | Rename-Item (Resolve-Path .\Modules\Module-Screening\Module-Screening.xml).Path -NewName (((Resolve-Path .\Modules\Module-Screening\Module-Screening.xml).Path).Replace('xml','bak'))
88 | } elseif ((Test-Path .\Modules\Module-Screening\Module-Screening.xml)) {
89 | Remove-Item (Resolve-Path .\Modules\Module-Screening\Module-Screening.xml).Path
90 | }
91 |
92 | # Second, we create our new file.
93 | $defaultXml = New-XmlContent -XmlFile .\Modules\Module-Screening\Module-Screening.xml
94 |
95 | # Third, we add our values.
96 | $defaultXml.WriteStartElement('Settings')
97 | $defaultXml.WriteStartElement('Format')
98 | $defaultXml.WriteStartElement('Title')
99 | $defaultXml.WriteAttributeString('ForegroundColor','Yellow')
100 | $defaultXml.WriteAttributeString('BackgroundColor','')
101 | $defaultXml.WriteAttributeString('Uppercase','Yes')
102 | $defaultXml.WriteAttributeString('Frame','*')
103 | $defaultXml.WriteEndElement()
104 | $defaultXml.WriteStartElement('Text')
105 | $defaultXml.WriteAttributeString('ForegroundColor','Gray')
106 | $defaultXml.WriteAttributeString('BackgroundColor','')
107 | $defaultXml.WriteAttributeString('Uppercase','No')
108 | $defaultXml.WriteAttributeString('Frame','|')
109 | $defaultXml.WriteEndElement()
110 | $defaultXml.WriteStartElement('Input')
111 | $defaultXml.WriteAttributeString('ForegroundColor','Cyan')
112 | $defaultXml.WriteAttributeString('BackgroundColor','')
113 | $defaultXml.WriteAttributeString('Uppercase','No')
114 | $defaultXml.WriteAttributeString('Frame','')
115 | $defaultXml.WriteEndElement()
116 | $defaultXml.WriteStartElement('Offer')
117 | $defaultXml.WriteAttributeString('ForegroundColor','DarkGray')
118 | $defaultXml.WriteAttributeString('BackgroundColor','')
119 | $defaultXml.WriteAttributeString('Uppercase','No')
120 | $defaultXml.WriteAttributeString('Frame','')
121 | $defaultXml.WriteEndElement()
122 | $defaultXml.WriteStartElement('Dynamic')
123 | $defaultXml.WriteStartElement('Disable')
124 | $defaultXml.WriteAttributeString('Color','DarkGray')
125 | $defaultXml.WriteEndElement()
126 | $defaultXml.WriteStartElement('Pending')
127 | $defaultXml.WriteAttributeString('Color','Gray')
128 | $defaultXml.WriteEndElement()
129 | $defaultXml.WriteStartElement('Running')
130 | $defaultXml.WriteAttributeString('Color','Cyan')
131 | $defaultXml.WriteEndElement()
132 | $defaultXml.WriteStartElement('Warning')
133 | $defaultXml.WriteAttributeString('Color','Yellow')
134 | $defaultXml.WriteEndElement()
135 | $defaultXml.WriteStartElement('Failure')
136 | $defaultXml.WriteAttributeString('Color','Red')
137 | $defaultXml.WriteEndElement()
138 | $defaultXml.WriteStartElement('Success')
139 | $defaultXml.WriteAttributeString('Color','Green')
140 | $defaultXml.WriteEndElement()
141 | $defaultXml.WriteEndElement()
142 | $defaultXml.WriteEndElement()
143 | $defaultXml.WriteEndElement()
144 |
145 | # finaly, we output the result to our files.
146 | $defaultXml.WriteEndDocument()
147 | $defaultXml.Flush()
148 | $defaultXml.Close()
149 | }
150 |
151 | Function Write-TitleText {
152 | <#
153 | .SYNOPSIS
154 | Echo a text as a title one.
155 |
156 | .DESCRIPTION
157 | Simple function to write a title on the screen.
158 | This function will rely on custom value from Module-Screening.xml.
159 |
160 | .PARAMETER Text
161 | A string or array of string that contains the title text.
162 |
163 | .NOTES
164 | Version: 01.000.000 -- Loic VEIRMAN (MSSec)
165 | History: 2024/05/11 -- Script creation.
166 | #>
167 |
168 | [CmdletBinding()]
169 | param (
170 | [Parameter(Mandatory,Position=0)]
171 | [Array]
172 | $Text
173 | )
174 |
175 | # No Logging.
176 | # Import XML settings data.
177 | $xmlSettings = Get-XmlContent .\modules\Module-Screening\Module-Screening.Xml
178 |
179 | if (-not ($xmlSettings)) {
180 | # Load as failed. We create a default one with our own values...
181 | New-ModuleScreeningXmlFile
182 | # Then we load it.
183 | $xmlSettings = Get-XmlContent .\modules\Module-Screening\Module-Screening.Xml
184 | }
185 |
186 | # Prepare write-host attributes
187 | $Attributes = @{}
188 | if ($xmlSettings.Settings.Format.Title.ForegroundColor) {
189 | $Attributes.Add('ForegroundColor',$xmlSettings.Settings.Format.Title.ForegroundColor)
190 | }
191 | if ($xmlSettings.Settings.Format.Title.BackgroundColor) {
192 | $Attributes.Add('BackgroundColor',$xmlSettings.Settings.Format.Title.BackgroundColor)
193 | }
194 |
195 | # Prepare Title Text
196 | $TitleText = Format-ScreenText $Text
197 | foreach ($line in $TitleText) {
198 | $FinalText += @("$($xmlSettings.Settings.Format.Title.Frame)$Line")
199 | }
200 |
201 | # Echo title text
202 | foreach ($line in $FinalText) {
203 | if ($xmlSettings.Format.Title.Uppercase -eq 'Yes') {
204 | $line = $line.ToUpper()
205 | }
206 | Write-Host $line @Attributes
207 | }
208 | }
209 |
210 | Function Write-InformationalText {
211 | <#
212 | .SYNOPSIS
213 | Echo a text as an information one.
214 |
215 | .DESCRIPTION
216 | Simple function to write an information on the screen.
217 | This function will rely on custom value from Module-Screening.xml.
218 |
219 | .PARAMETER Text
220 | A string or array of string that contains the title text.
221 |
222 | .NOTES
223 | Version: 01.000.000 -- Loic VEIRMAN (MSSec)
224 | History: 2024/05/11 -- Script creation.
225 | #>
226 |
227 | [CmdletBinding()]
228 | param (
229 | [Parameter(Mandatory,Position=0)]
230 | [Array]
231 | $Text
232 | )
233 |
234 | # No Logging.
235 | # Import XML settings data.
236 | $xmlSettings = Get-XmlContent .\modules\Module-Screening\Module-Screening.Xml
237 |
238 | if (-not ($xmlSettings)) {
239 | # Load as failed. We create a default one with our own values...
240 | New-ModuleScreeningXmlFile
241 | # Then we load it.
242 | $xmlSettings = Get-XmlContent .\modules\Module-Screening\Module-Screening.Xml
243 | }
244 |
245 | # Prepare write-host attributes
246 | $Attributes = @{}
247 | if ($xmlSettings.Settings.Format.Text.ForegroundColor) {
248 | $Attributes.Add('ForegroundColor',$xmlSettings.Settings.Format.Text.ForegroundColor)
249 | }
250 | if ($xmlSettings.Settings.Format.Input.BackgroundColor) {
251 | $Attributes.Add('BackgroundColor',$xmlSettings.Settings.Format.Text.BackgroundColor)
252 | }
253 |
254 | # Prepare Title Text
255 | $InfoText = Format-ScreenText $Text
256 | foreach ($line in $InfoText) {
257 | $FinalText += @("$($xmlSettings.Settings.Format.Text.Frame)$Line")
258 | }
259 |
260 | # Echo title text
261 | foreach ($line in $FinalText) {
262 | if ($xmlSettings.Format.Text.Uppercase -eq 'Yes') {
263 | $line = $line.ToUpper()
264 | }
265 | Write-Host $line @Attributes
266 | }
267 | }
268 |
269 | Function Write-UserChoice {
270 | <#
271 | .SYNOPSIS
272 | Echo a text and ask for a Yes or No choice.
273 |
274 | .DESCRIPTION
275 | Simple function to write an information on the screen and ask for a choice.
276 | This function will rely on custom value from Module-Screening.xml.
277 |
278 | .PARAMETER Text
279 | A string or array of string that contains the question text (Line1) and the keys option (Line2).
280 |
281 | .NOTES
282 | Version: 01.000.000 -- Loic VEIRMAN (MSSec)
283 | History: 2024/05/11 -- Script creation.
284 | #>
285 |
286 | [CmdletBinding()]
287 | param (
288 | [Parameter(Mandatory,Position=0)]
289 | [Array]
290 | $Text
291 | )
292 |
293 | # No Logging.
294 | # Import XML settings data.
295 | $xmlSettings = Get-XmlContent .\modules\Module-Screening\Module-Screening.Xml
296 |
297 | if (-not ($xmlSettings)) {
298 | # Load as failed. We create a default one with our own values...
299 | New-ModuleScreeningXmlFile
300 | # Then we load it.
301 | $xmlSettings = Get-XmlContent .\modules\Module-Screening\Module-Screening.Xml
302 | }
303 |
304 | # Prepare write-host attributes: question text
305 | $qAttributes = @{}
306 | if ($xmlSettings.Settings.Format.Input.ForegroundColor) {
307 | $qAttributes.Add('ForegroundColor',$xmlSettings.Settings.Format.Input.ForegroundColor)
308 | }
309 | if ($xmlSettings.Settings.Format.Input.BackgroundColor) {
310 | $qAttributes.Add('BackgroundColor',$xmlSettings.Settings.Format.Input.BackgroundColor)
311 | }
312 |
313 | # Prepare write-host attributes: choice text
314 | $cAttributes = @{}
315 | if ($xmlSettings.Settings.Format.Text.ForegroundColor) {
316 | $cAttributes.Add('ForegroundColor',$xmlSettings.Settings.Format.Text.ForegroundColor)
317 | }
318 | if ($xmlSettings.Settings.Format.Text.BackgroundColor) {
319 | $cAttributes.Add('BackgroundColor',$xmlSettings.Settings.Format.Text.BackgroundColor)
320 | }
321 |
322 | # Prepare Question Text
323 | $InfoText = Format-ScreenText $Text[0]
324 | foreach ($line in $InfoText) {
325 | $FinalText += @("$($xmlSettings.Settings.Format.Input.Frame)$Line")
326 | }
327 |
328 | # Echo Question
329 | $LastLine = $FinalText.Count
330 | $i = 1
331 | foreach ($line in $FinalText) {
332 | if ($xmlSettings.Format.Input.Uppercase -eq 'Yes') {
333 | $line = $line.ToUpper()
334 | }
335 | if ($i -eq $LastLine) {
336 | Write-Host $line @qAttributes -NoNewline
337 | }
338 | Else {
339 | Write-Host $line @qAttributes
340 | }
341 | }
342 |
343 | # Echo Choice
344 | Write-host " $($Text[1])" @cAttributes -NoNewline
345 | }
346 |
347 | Function Write-WarningText {
348 | <#
349 | .SYNOPSIS
350 | Will write a custom warning text on script.
351 |
352 | .DESCRIPTION
353 | Rad the ScriptSettings.xml file to display and colorize a text based on a specific typo
354 | > `[mon text`: display "mon text" in a first specific color (color is set in the section).
355 | > `{mon text`: display "mon text" in a second specific color (color is set in the section).
356 | > `|mon text`: display "mon text" in a third specific color (color is set in the section).
357 |
358 | When no code is set, the script use the Default color form the scheme.
359 |
360 | Return True if the user choose to leave the script.
361 |
362 | .PARAMETER Id
363 | The Section to look after in the xml file.
364 | #>
365 | [CmdletBinding()]
366 | param (
367 | [Parameter(Mandatory,Position=0)]
368 | [ValidateSet('RebootAction','FinalAction')]
369 | [String]
370 | $Id
371 | )
372 |
373 | # No Logging.
374 | # Loading Script Settings
375 | $ScriptSettings = Get-XmlContent .\configuration\ScriptSettings.xml
376 |
377 | # Loading Text data
378 | $TextData = $ScriptSettings.Settings.Warning.$Id
379 |
380 | # Setting-Up Color Scheme
381 | $ColorScheme = $TextData.ColorScheme
382 | $ColorData = $ScriptSettings.settings.ColorScheme.$ColorScheme
383 | $ColorA = $ColorData.A
384 | $ColorB = $ColorData.B
385 | $ColorC = $ColorData.C
386 | $ColorDefault = $ColorData.Default
387 |
388 | # Setting-Up Text to Display
389 | $displayLines = @()
390 | foreach ($line in $TextData.Line) {
391 | # Adapt text to screen
392 | $displayLines += Format-ScreenText $line
393 | }
394 |
395 | # Display Text on screen
396 | foreach ($displayLine in $displayLines) {
397 | # Wrapping up line in sequence for color formating
398 | $myBlocks = $displayLine -split '`'
399 |
400 | # Displaying result per block
401 | foreach ($myBlock in $myBlocks) {
402 | # Getting color based on first char of the string
403 | Switch ($myBlock[0]) {
404 | # Color A
405 | '{' {
406 | $textColor = $ColorA
407 | $myBlock = $myBlock.Substring(1,$myBlock.length - 1)
408 | }
409 | # Color B
410 | '[' {
411 | $textColor = $ColorB
412 | $myBlock = $myBlock.Substring(1,$myBlock.length - 1)
413 | }
414 | # Color C
415 | '|' {
416 | $textColor = $ColorC
417 | $myBlock = $myBlock.Substring(1,$myBlock.length - 1)
418 | }
419 | # Color default
420 | Default {
421 | $textColor = $ColorDefault
422 | }
423 | }
424 | Write-Host $myBlock -ForegroundColor $textColor -NoNewline
425 | }
426 | # Last block? Next line.
427 | Write-Host
428 | }
429 |
430 | # Dealing with a confirmation requierement
431 | if ($TextData.confirm -eq "Yes") {
432 | # Prompting user to press a key before continuing. Esc or Q will be considered as a quit.
433 | Write-Host
434 | Write-Host "Press a key to continue (ESC or Q to leave)" -ForegroundColor red -BackgroundColor yellow
435 | Write-Host
436 | $Key = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
437 |
438 | if ($Key.VirtualKeyCode -eq 27 -or $Key.VirtualKeyCode -eq 81) {
439 | # Leaving order sent back.
440 | $result = $true
441 | }
442 | Else {
443 | $result = $false
444 | }
445 | }
446 |
447 | # Return result
448 | return $result
449 | }
450 |
451 | Function Write-Progression {
452 | <#
453 | .SYNOPSIS
454 | Display the progression status
455 |
456 | .DESCRIPTION
457 | Display the progression status on screen. The function returns the Cursor position when using the create step (else nothing).
458 |
459 | .PARAMETER Step
460 | >Create: will set a new line on screen.
461 | >Update: will update an existing line on screen.
462 |
463 | .PARAMETER Code
464 | Which action result should be displayed on screen.
465 |
466 | .PARAMETER Message
467 | Line text to show on screen.
468 |
469 | .PARAMETER CursorPosition
470 | .Net object containing current cursor position for script writing.
471 |
472 | .EXAMPLE
473 | .\Write-Progression Create 'This is an exemple'
474 | Will output: [ ] This is an example
475 |
476 | .EXAMPLE
477 | .\Write-Progression running $coordinate
478 | Will output: [running] This is an example
479 |
480 | .NOTES
481 | Version 1.0 (Creation - 2024/06/29)
482 | #>
483 | Param(
484 | [Parameter(Position=0,ParameterSetName='new')]
485 | [Parameter(Position=0,ParameterSetName='update')]
486 | [ValidateSet('Create','Update')]
487 | [String]$Step,
488 |
489 | [Parameter(Position=1,ParameterSetName='new')]
490 | [String]$Message,
491 |
492 | [Parameter(Position=1,ParameterSetName='update')]
493 | [ValidateSet('running','success','warning','error')]
494 | [String]$Code,
495 |
496 | [Parameter(Mandatory,Position=2,ParameterSetName='update')]
497 | $CursorPosition
498 | )
499 | # No logging.
500 | # Variables for this function
501 | $arrayTextColor = @{"running"="cyan";"success"="green";"warning"="yellow";"error"="red";"text"="Gray";"history"="darkgray"}
502 | $arrayTextDplay = @{"running"="running";"success"="success";"warning"="warning";"error"=" error ";"text"="[ ] "}
503 |
504 | Switch ($Step)
505 | {
506 | # This is a new line
507 | "Create"
508 | {
509 | # Getting the current cursor position
510 | $CursorPosition = $Host.UI.RawUI.CursorPosition
511 | # Displaying blank output
512 | Write-Host $arrayTextDplay["text"] -ForegroundColor $arrayTextColor["text"] -NoNewline
513 | # Adding text message
514 | Write-Host $Message -ForegroundColor $arrayTextColor["text"] -NoNewline
515 | # Return cursor position
516 | return $CursorPosition
517 | }
518 | # Updating a line
519 | "Update"
520 | {
521 | # Move cursor to new coordinates
522 | $Host.UI.RawUI.CursorPosition = New-Object System.Management.Automation.Host.Coordinates ($CursorPosition.X + 1), $CursorPosition.Y
523 | # write new status
524 | Write-Host $arrayTextDplay[$Code] -ForegroundColor $arrayTextColor[$Code]
525 | }
526 | }
527 | }
--------------------------------------------------------------------------------
/Modules/Module-Screening/Module-Screening.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/Modules/Module-Xml/Module-Xml.psd1:
--------------------------------------------------------------------------------
1 | #
2 | # Module manifest for module 'Module-Xml'
3 | #
4 | # Generated by: Loic VEIRMAN (MSSec)
5 | #
6 | # Generated on: 09/05/2024
7 | #
8 |
9 | @{
10 |
11 | # Script module or binary module file associated with this manifest.
12 | RootModule = 'Module-Xml.psm1'
13 |
14 | # Version number of this module.
15 | ModuleVersion = '1.0.0.0'
16 |
17 | # Supported PSEditions
18 | # CompatiblePSEditions = @()
19 |
20 | # ID used to uniquely identify this module
21 | GUID = 'c6cc76f9-cd11-4dfa-b88b-ba919461c7cc'
22 |
23 | # Author of this module
24 | Author = 'Loic VEIRMAN (MSSec)'
25 |
26 | # Company or vendor of this module
27 | CompanyName = 'MSSec'
28 |
29 | # Copyright statement for this module
30 | Copyright = '(c) Loic VEIRMAN (MSSec). All rights reserved.'
31 |
32 | # Description of the functionality provided by this module
33 | Description = 'This module is intended to manage log entries to a file or the eventViewer'
34 |
35 | # Minimum version of the PowerShell engine required by this module
36 | PowerShellVersion = '5.0'
37 |
38 | # Name of the PowerShell host required by this module
39 | # PowerShellHostName = ''
40 |
41 | # Minimum version of the PowerShell host required by this module
42 | # PowerShellHostVersion = ''
43 |
44 | # Minimum version of Microsoft .NET Framework required by this module. This prerequisite is valid for the PowerShell Desktop edition only.
45 | # DotNetFrameworkVersion = ''
46 |
47 | # Minimum version of the common language runtime (CLR) required by this module. This prerequisite is valid for the PowerShell Desktop edition only.
48 | # ClrVersion = ''
49 |
50 | # Processor architecture (None, X86, Amd64) required by this module
51 | # ProcessorArchitecture = ''
52 |
53 | # Modules that must be imported into the global environment prior to importing this module
54 | # RequiredModules = @()
55 |
56 | # Assemblies that must be loaded prior to importing this module
57 | # RequiredAssemblies = @()
58 |
59 | # Script files (.ps1) that are run in the caller's environment prior to importing this module.
60 | # ScriptsToProcess = @()
61 |
62 | # Type files (.ps1xml) to be loaded when importing this module
63 | # TypesToProcess = @()
64 |
65 | # Format files (.ps1xml) to be loaded when importing this module
66 | # FormatsToProcess = @()
67 |
68 | # Modules to import as nested modules of the module specified in RootModule/ModuleToProcess
69 | # NestedModules = @('Module-Xml')
70 |
71 | # Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export.
72 | FunctionsToExport = '*'
73 |
74 | # Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export.
75 | CmdletsToExport = @()
76 |
77 | # Variables to export from this module
78 | VariablesToExport = '*'
79 |
80 | # Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export.
81 | AliasesToExport = @()
82 |
83 | # DSC resources to export from this module
84 | # DscResourcesToExport = @()
85 |
86 | # List of all modules packaged with this module
87 | # ModuleList = @()
88 |
89 | # List of all files packaged with this module
90 | # FileList = @()
91 |
92 | # Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell.
93 | PrivateData = @{
94 |
95 | PSData = @{
96 |
97 | # Tags applied to this module. These help with module discovery in online galleries.
98 | # Tags = @()
99 |
100 | # A URL to the license for this module.
101 | # LicenseUri = ''
102 |
103 | # A URL to the main website for this project.
104 | # ProjectUri = ''
105 |
106 | # A URL to an icon representing this module.
107 | # IconUri = ''
108 |
109 | # ReleaseNotes of this module
110 | ReleaseNotes = 'Module Creation'
111 |
112 | # Prerelease string of this module
113 | # Prerelease = ''
114 |
115 | # Flag to indicate whether the module requires explicit user acceptance for install/update/save
116 | # RequireLicenseAcceptance = $false
117 |
118 | # External dependent modules of this module
119 | # ExternalModuleDependencies = @()
120 |
121 | } # End of PSData hashtable
122 |
123 | } # End of PrivateData hashtable
124 |
125 | # HelpInfo URI of this module
126 | # HelpInfoURI = ''
127 |
128 | # Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix.
129 | # DefaultCommandPrefix = ''
130 |
131 | }
132 |
133 |
--------------------------------------------------------------------------------
/Modules/Module-Xml/Module-Xml.psm1:
--------------------------------------------------------------------------------
1 | <#
2 | THIS MODULE CONTAINS FUNCTIONS RELATED TO XML HANDLING (READ, MODIFY, ...)
3 | #>
4 |
5 | Function Get-XmlContent {
6 | <#
7 | .SYNOPSIS
8 | Return an XML object to the caller.
9 |
10 | .DESCRIPTION
11 | Will try to open the specified file, then return it to the caller. If it fails, the function returns a Null object.
12 |
13 | .PARAMETER XmlFile
14 | Path to the XML file to import. You can use relative or fixed path.
15 |
16 | .NOTES
17 | Version: 01.000.000 -- Loic VEIRMAN (MSSec)
18 | History: 2024/05/08 -- Script creation.
19 | #>
20 |
21 | Param(
22 | # Xml file path and name
23 | [Parameter(mandatory, Position=0)]
24 | [String]
25 | $XmlFile
26 | )
27 | # Module logging requiered, hence we check il we need to load it first.
28 |
29 | # Convert to fixed path
30 | $xFile = Resolve-Path -Path $XmlFile -ErrorAction SilentlyContinue
31 |
32 | # Check if the xml file is reachable
33 | Try {
34 | if (Test-Path $xFile -ErrorAction Stop) {
35 | # File is present, we will load it
36 | $xmlData = [xml](Get-Content $xFile -Encoding utf8 -ErrorAction Stop)
37 | }
38 | Else {
39 | # File is unreachable
40 | $xmlData = $null
41 | }
42 | }
43 | Catch {
44 | $xmlData = $null
45 | }
46 |
47 | # Return result
48 | return $xmlData
49 | }
50 |
51 | Function New-XmlContent {
52 | <#
53 | .SYNOPSIS
54 | Create a xml file and return an xml object for data manipulation.
55 |
56 | .DESCRIPTION
57 | Create a xml file and return an xml object to manipulate its content.
58 |
59 | .PARAMETER XmlFile
60 | Path to the XML file to import. You can use relative or fixed path.
61 |
62 | .NOTES
63 | Version: 01.000.000 -- Loic VEIRMAN (MSSec)
64 | History: 2024/05/10 -- Script creation.
65 | #>
66 |
67 | Param(
68 | # Xml file path and name
69 | [Parameter(mandatory, Position=0)]
70 | [String]
71 | $XmlFile
72 | )
73 | # Prepare for debug log. Only one entrie in event log for the whole function.
74 | Test-EventLog | Out-Null
75 | $DbgLog = @("Function caller: $(((Get-PSCallStack)[1].Command -split '\.')[0])"," ")
76 |
77 | # Test if the file already exists. If so, return a null object.
78 | Try {
79 | if (Test-Path (Resolve-Path $XmlFile -ErrorAction Stop)) {
80 | $DbgLog += "Error: the file could not created as it already exists."
81 | $DbgType = "ERROR"
82 | $result = $null
83 | }
84 | Else {
85 | $DbgLog += @("New file creation: $(Resolve-Path $XmlFile)"," ","Encoding: UTF8", "Indent: Yes (tabulation)")
86 | $DbgType = "INFO"
87 |
88 | # Formating XML
89 | $xmlSettings = New-Object System.Xml.XmlWriterSettings
90 | $xmlSettings.Indent = $true
91 | $xmlSettings.IndentChars = "`t"
92 | $xmlSettings.Encoding = [System.Text.Encoding]::UTF8
93 |
94 | # Create the document
95 | $XmlWriter = [System.XML.XmlWriter]::Create((Resolve-Path $XmlFile), $xmlSettings)
96 |
97 | # Write the XML Decleration and set the XSL
98 | $xmlWriter.WriteStartDocument()
99 | $xmlWriter.WriteProcessingInstruction("xml-stylesheet", "type='text/xsl' href='style.xsl'")
100 |
101 | # Return the object handler
102 | $result = $XmlWriter
103 | }
104 | }
105 | Catch {
106 | $DbgLog += @("New file creation: $($XmlFile)"," ","Encoding: UTF8", "Indent: Yes (tabulation)")
107 | $DbgType = "INFO"
108 |
109 | # Formating XML
110 | $xmlSettings = New-Object System.Xml.XmlWriterSettings
111 | $xmlSettings.Indent = $true
112 | $xmlSettings.IndentChars = "`t"
113 | $xmlSettings.Encoding = [System.Text.Encoding]::UTF8
114 |
115 | # Get Current Directory
116 | $BaseDir = (Resolve-Path .).Path
117 | $newXmlFile = "$BaseDir$($XmlFile.Substring(1))"
118 | # Create the document
119 | $XmlWriter = [System.XML.XmlWriter]::Create($newXmlFile, $xmlSettings)
120 |
121 | # Write the XML Decleration and set the XSL
122 | $xmlWriter.WriteStartDocument()
123 | $xmlWriter.WriteProcessingInstruction("xml-stylesheet", "type='text/xsl' href='style.xsl'")
124 |
125 | # Return the object handler
126 | $result = $XmlWriter
127 | }
128 | # Writing log
129 | Write-toEventLog $DbgType $DbgLog | Out-Null
130 |
131 | # Return result
132 | return $result
133 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | # Hello My Dir!
4 | #### Release 01.01.02.002 - *Hello My DC!*
5 |
6 | ## Important notice
7 | You should always update your existing HmD repository with the latest edition and run the below command to adapt your configuration file:
8 | ```PS
9 | Invoke-HelloMyDir.ps1 -UpdateConfigFile
10 | ```
11 |
12 | ## They talk about it (and we thanks them ;))
13 | https://www.it-connect.fr/comment-creer-un-domaine-active-directory-respectueux-des-bonnes-pratiques-de-securite/
14 |
15 | ## Project description
16 | This project is specifically made for brand new directories and ease their creation with all security rules in place:
17 | > - Remove legacy protocols/setup used by Microsoft for compliance purposes
18 | > - Enforce the use of modern alogrithm for cyphering and authentication
19 | > - Enforce LDAPS when a client requests a connection to your DC
20 | > - Enforce the default password strategy to match with modern expectation
21 | > - Add other Domain Controllers to your secured domain
22 |
23 | The script will automate the answer file by itself at first run, but can modify it by using the parameter *-Prepare*.
24 | The documentation is in place in the folder "Documentation" and explain how you can run it.
25 |
26 | ## Release history
27 | **01.01.02: Quick, Fix and Gone...**
28 | > - QuickFix 001: Fixed a mistyping line 579 that badly report success when installing mandatory binaries.
29 | > - QuickFix 002: Fixed bad reporting when deploying or testing binaries - Thanks to JM2K69 Jérôme Bezet-Torres [Alumni - MVP].
30 |
31 | **01.01.01: Self-Signed Certificate Update**
32 | > - the self-signed certificate now contains DC Name, DC Full-Qualified Domain Name and Domain name as cert name and alternatives.
33 |
34 | **01.01.00: Hello My DC!**
35 | > - Add the ability to promote a Domain Controller in your domain.
36 | > - Add a new group named "*LS-DELEG-DomainJoin-Extended*" intended to delegate right on computer objet at location *CN=Computers,DC-Your,DC=Domain*.
37 | > - Add a new user named "*DLGUSER01*" intended to join computer to the domain. The user is a member of "*LS-DELEG-DomainJoin-Extended*" and have a PSO applied on it (*PSO-ServiceAccounts-ADdelegatedRight*).
38 | > - Set a delegation on *CN=Computers,DC-Your,DC=Domain* to allow "*LS-DELEG-DomainJoin-Extended*"'s group members to manage computer objects (domain joining).
39 |
40 | **01.00.00: Hello My Dir!**
41 | > - Script creation. Allow you to create a brand new domain/forest fully secured.
42 |
43 | ## Auditing with Ping Castle and Purple Knight
44 | While diving around the script, we have ensured that both well known AD security auditing tools will give you the maximum score you can expect right after building up your domain.
45 | To achieve our goal, we have tested our delivery against the below versions of their respective Community Edition:
46 | > - Ping Castle 3.2.0.1
47 | > - Purple Knight 4.2
48 |
49 | Tests were made upon Windows Servers 2016 to 2022 (English edition), and Functional Level were tested from 2008 up to 2022.
50 |
51 | ## Does it be enough for securing AD?
52 | Certainly... Not. Well, securing AD is a journey and depend on whatever you want to do with (or associate with).
53 | This project is a good starting point however, but it will be up to you to maintain it at the best level.
54 | The first two things you will have to do is:
55 | 1. Build a second Domain Controller to fulfill redondancy requirement
56 | 2. Assign to the second Domain Controller,a self-signed certificate (at least) to enable LDAPS (you can reuse the code from the function *Resolve-LDAPSrequired*).
57 |
58 | Then, we strongly recommend you to define a Tier Model Policy, such as the one we have built through the Harden AD project (https://hardenad.net).
59 |
60 | Of course, you still can contact us to assist you in elaborating your brand new Active Directory: we'll be glad to help!
61 |
62 | ## Why do you create this project, when harden AD may be able to do the trick?
63 | It's a good question. The short answer will be: *to avoid complexity*. But here's the long story...
64 | When creating my own labs (which I do quite often), I use to run my own scripts in a specific order to create a new AD, create an OU topology, add users, groups and many more other minor things.
65 | Then, once everything is ready, I turn on HardenAD. It's not fun, it's a waste of time, blah blah blah... But it cools my brain when I need to rest or get angry over a piece of code.
66 | So, let's have some fun: let's create a script that does all of this in one way!
67 | That's when I suddenly realized that this might also be the most effective tool we can offer the AD community for hardening the security of a default directory: by making it usable for production...
68 |
69 | ## Will you merge the two projects (*harden AD* and *Hello my Dir*) one day?
70 | Why not? However, we are not thinking about it yet. Let's first see how this project will unfold...
71 |
72 | ## Can we use your project to build AD for our customers?
73 | You can sell an Active Directory installation service using this project by explicitly mentioning the Harden community as the author of the latter and of the deployed model (security, etc.) and by crediting the authors of this project. By doing so, you will help improve the visibility of the community and strengthen its notoriety, which will benefit even more people for a safer AD environment!
74 |
75 | ## Any last word?
76 | "Aaaaaargggghhhh..." - the Killing Joke.
77 |
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 | # Security Policy
2 |
3 | ## Supported Versions
4 |
5 | Use this section to tell people about which versions of your project are
6 | currently being supported with security updates.
7 |
8 | | Version | Supported |
9 | | ------- | ------------------ |
10 | | 5.1.x | :white_check_mark: |
11 | | 5.0.x | :x: |
12 | | 4.0.x | :white_check_mark: |
13 | | < 4.0 | :x: |
14 |
15 | ## Reporting a Vulnerability
16 |
17 | Use this section to tell people how to report a vulnerability.
18 |
19 | Tell them where to go, how often they can expect to get an update on a
20 | reported vulnerability, what to expect if the vulnerability is accepted or
21 | declined, etc.
22 |
--------------------------------------------------------------------------------
/prepare-test.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | .Synopsis
3 | Prepare system ofr a test run
4 | #>
5 |
6 | Param(
7 | [switch]$KeepRunSetup,
8 | [switch]$RemovePrerequesite,
9 | [string]$InstallPath = "C:\HmD"
10 | )
11 |
12 | # Backup config file
13 | if ($KeepRunSetup) {
14 | if (Test-Path $InstallPath\Configuration\RunSetup.xml) {
15 | $backup = [XML](get-content $InstallPath\Configuration\RunSetup.xml -Encoding UTF8)
16 | }
17 | }
18 |
19 | # Updating binaries
20 | Robocopy.exe "$((Get-Location).Path)" "$($InstallPath)" /MIR
21 |
22 | # Restore config file
23 | if ($KeepRunSetup) {
24 | $backup | Out-File $InstallPath\Configuration\RunSetup.xml -Encoding UTF8
25 | }
26 |
27 | # Cleaning up any existing ADDS installation (and reboot if needed)
28 | Try {
29 | [void]($DCinstalled = Get-AdDomain -ErrorAction Stop)
30 | }
31 | Catch {
32 | $DCinstalled = $null
33 | }
34 | if ($DCinstalled) {
35 | Try {
36 | Uninstall-ADDSDomainController -LastDomainControllerInDomain -LocalAdministratorPassword (ConvertTo-SecureString -AsPlainText "C1mo2pasS==" -Force) -RemoveApplicationPartitions -confirm:$false -Reboot
37 | }
38 | Catch {
39 | Write-Warning "Failed to uninstall ADDS: $($_.ToString())"
40 | }
41 | Write-host "Wait for reboot and rerun the script" -ForegroundColor Yellow
42 | Exit 0
43 | }
44 |
45 | if ($RemovePrerequesite) {
46 | #Dealing with binaries to uninstall
47 | $reqBinaries = @('AD-Domain-Services', 'RSAT-AD-Tools', 'RSAT-DNS-Server', 'RSAT-DFS-Mgmt-Con', 'GPMC')
48 | $ProgressPreference = "SilentlyContinue"
49 |
50 | foreach ($ReqBinary in $reqBinaries) {
51 | Try {
52 | Uninstall-WindowsFeature -Name $ReqBinary -IncludeManagementTools -ErrorAction Stop
53 | }
54 | Catch {
55 | Write-Warning "Failed to uninstall $ReqBinary (error: $($_.ToString()))"
56 | }
57 | }
58 | }
59 |
60 | exit 0
--------------------------------------------------------------------------------