├── .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[Software\Policies\Microsoft\Windows\NetworkProvider\HardenedPaths;\\*\SYSVOL;;d;RequireMutualAuthentication=1, RequireIntegrity=1][Software\Policies\Microsoft\Windows\NetworkProvider\HardenedPaths;\\*\NETLOGON;;d;RequireMutualAuthentication=1, RequireIntegrity=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[SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\LAPS;ADBackupDSRMPassword;;;][SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\LAPS;ADPasswordEncryptionEnabled;;;][SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\LAPS;BackupDirectory;;;][SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\LAPS;PasswordExpirationProtectionEnabled;;;][SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\LAPS;PasswordComplexity;;;][SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\LAPS;PasswordLength;;;][SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\LAPS;PasswordAgeDays;;;][SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\LAPS;PostAuthenticationResetDelay;;;][SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\LAPS;PostAuthenticationActions;;;][SOFTWARE\Policies\Microsoft\Windows\PowerShell\ModuleLogging;EnableModuleLogging;;;][SOFTWARE\Policies\Microsoft\Windows\PowerShell\ModuleLogging\ModuleNames;**delvals.;;; ][SOFTWARE\Policies\Microsoft\Windows\PowerShell\ModuleLogging\ModuleNames;*;;;*][SOFTWARE\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging;EnableScriptBlockLogging;;;][SOFTWARE\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging;EnableScriptBlockInvocationLogging;;;][SOFTWARE\Policies\Microsoft\Windows NT\DNSClient;EnableMulticast;;;] -------------------------------------------------------------------------------- /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 | <Text ForegroundColor='White' BackgroundColor='' Uppercase='No' Frame='|' /> 6 | <Input ForegroundColor='Cyan' BackgroundColor='' Uppercase='No' Frame=' ' /> 7 | <Offer ForegroundColor='darkGray' BackgroundColor='' Uppercase='No' Frame='' /> 8 | <Dynamic> 9 | <Disable color="darGray" /> 10 | <Pending color="gray" /> 11 | <Running color="Cyan" /> 12 | <Warning color="Yellow" /> 13 | <Failure color="Red" /> 14 | <Success color="Green" /> 15 | </Dynamic> 16 | </Format> 17 | </Settings> -------------------------------------------------------------------------------- /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 | ![logo hello my dir baniere fond gris](https://github.com/user-attachments/assets/83c3f729-8adc-4084-9765-51318256cbb6) 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 --------------------------------------------------------------------------------