├── ADMF ├── internal │ ├── data │ │ ├── context │ │ │ ├── domain │ │ │ │ ├── gpowners │ │ │ │ │ └── readme.md │ │ │ │ ├── gppermissionfilters │ │ │ │ │ └── readme.md │ │ │ │ ├── acls │ │ │ │ │ └── readme.md │ │ │ │ ├── users │ │ │ │ │ └── readme.md │ │ │ │ ├── gplinks │ │ │ │ │ └── readme.md │ │ │ │ ├── groups │ │ │ │ │ └── readme.md │ │ │ │ ├── accessrules │ │ │ │ │ └── readme.md │ │ │ │ ├── builtinsids │ │ │ │ │ └── readme.md │ │ │ │ ├── psos │ │ │ │ │ └── readme.md │ │ │ │ ├── groupmemberships │ │ │ │ │ └── readme.md │ │ │ │ ├── organizationalunits │ │ │ │ │ └── readme.md │ │ │ │ ├── grouppolicies │ │ │ │ │ └── readme.md │ │ │ │ ├── objects │ │ │ │ │ └── readme.md │ │ │ │ ├── wmifilter │ │ │ │ │ └── readme.md │ │ │ │ ├── names │ │ │ │ │ └── readme.md │ │ │ │ ├── objectcategories │ │ │ │ │ └── readme.md │ │ │ │ ├── exchange │ │ │ │ │ └── readme.md │ │ │ │ ├── gppermissions │ │ │ │ │ └── readme.md │ │ │ │ ├── domaindata │ │ │ │ │ └── readme.md │ │ │ │ ├── readme.md │ │ │ │ ├── gpregistrysettings │ │ │ │ │ └── readme.md │ │ │ │ └── accessrulemodes │ │ │ │ │ └── readme.md │ │ │ ├── postImport.ps1 │ │ │ ├── preImport.ps1 │ │ │ ├── forest │ │ │ │ ├── sites │ │ │ │ │ └── readme.md │ │ │ │ ├── subnets │ │ │ │ │ └── readme.md │ │ │ │ ├── sitelinks │ │ │ │ │ └── readme.md │ │ │ │ ├── schema │ │ │ │ │ └── readme.md │ │ │ │ ├── servers │ │ │ │ │ └── readme.md │ │ │ │ ├── schemaldif │ │ │ │ │ └── readme.md │ │ │ │ ├── readme.md │ │ │ │ ├── ntAuthStore │ │ │ │ │ └── readme.md │ │ │ │ ├── exchangeschema │ │ │ │ │ └── readme.md │ │ │ │ └── schemaDefaultPermissions │ │ │ │ │ └── readme.md │ │ │ ├── contextPromptChecked.ps1 │ │ │ └── dc │ │ │ │ ├── readme.md │ │ │ │ ├── shares │ │ │ │ └── readme.md │ │ │ │ └── fsaccessrules │ │ │ │ └── readme.md │ │ ├── domainDefaults │ │ │ ├── gppermissionfilters │ │ │ │ ├── readme.md │ │ │ │ └── addefault_DefaultDomainPolicy.json │ │ │ ├── objectCategories │ │ │ │ ├── addefault_domainControllers.psd1 │ │ │ │ ├── addefault_msDFSR-LocalSettings.psd1 │ │ │ │ ├── addefault_domainSystemVolume.psd1 │ │ │ │ ├── addefault_sysvolSubscription.psd1 │ │ │ │ ├── addefault_serviceAccount.psd1 │ │ │ │ ├── addefault_virtualMachines.psd1 │ │ │ │ └── addefault_systemDomainUpdates.psd1 │ │ │ ├── acls │ │ │ │ ├── addefault_default.json │ │ │ │ └── addefault_domaincontroller.json │ │ │ ├── accessRules │ │ │ │ ├── addefault_virtualMachines.json │ │ │ │ ├── addefault_systemDomainUpdates.psd1 │ │ │ │ ├── addefault_domainObject.json │ │ │ │ ├── addefault_cn_Infrastructure.json │ │ │ │ ├── addefault_cn_Computers.json │ │ │ │ ├── addefault_domainControllers.json │ │ │ │ └── addefault_cn_Keys.json │ │ │ └── gppermissions │ │ │ │ ├── addefault_allPermissions.json │ │ │ │ └── readme.md │ │ ├── exchangeSPDefaults │ │ │ ├── accessRules │ │ │ │ ├── msExchangeSystemMailbox.psd1 │ │ │ │ ├── msExchangeSGLegacyInterop.json │ │ │ │ ├── msExchangeSGOrgManagement.json │ │ │ │ ├── msExchangeSGPubFolManagement.json │ │ │ │ ├── msExchangeOtherObjects.psd1 │ │ │ │ ├── msExchangeSGExchangeServers.json │ │ │ │ └── msExchangeSecurityGroups.json │ │ │ └── objectCategories │ │ │ │ └── msExchangeSystemMailbox.psd1 │ │ └── exchangeDefaults │ │ │ └── accessRules │ │ │ ├── msExchangeSGLegacyInterop.json │ │ │ ├── msExchangeSGOrgManagement.json │ │ │ ├── msExchangeSGPubFolManagement.json │ │ │ ├── msExchangeSGExchangeServers.json │ │ │ └── msExchangeSecurityGroups.json │ ├── components │ │ ├── DefaultAccessRules │ │ │ └── domain │ │ │ │ ├── gppermissionfilters │ │ │ │ ├── readme.md │ │ │ │ └── addefault_DefaultDomainPolicy.json │ │ │ │ ├── objectCategories │ │ │ │ ├── addefault_domainControllers.psd1 │ │ │ │ ├── addefault_msDFSR-LocalSettings.psd1 │ │ │ │ ├── addefault_domainSystemVolume.psd1 │ │ │ │ ├── addefault_sysvolSubscription.psd1 │ │ │ │ ├── addefault_serviceAccount.psd1 │ │ │ │ ├── addefault_virtualMachines.psd1 │ │ │ │ └── addefault_systemDomainUpdates.psd1 │ │ │ │ ├── acls │ │ │ │ ├── addefault_default.json │ │ │ │ └── addefault_domaincontroller.json │ │ │ │ ├── accessRules │ │ │ │ ├── addefault_virtualMachines.json │ │ │ │ ├── addefault_systemDomainUpdates.psd1 │ │ │ │ ├── addefault_domainObject.json │ │ │ │ ├── addefault_cn_Infrastructure.json │ │ │ │ ├── addefault_cn_Computers.json │ │ │ │ ├── addefault_domainControllers.json │ │ │ │ └── addefault_cn_Keys.json │ │ │ │ └── gppermissions │ │ │ │ ├── addefault_allPermissions.json │ │ │ │ └── readme.md │ │ ├── ExchangeSPDefaults │ │ │ └── domain │ │ │ │ ├── accessRules │ │ │ │ ├── msExchangeSystemMailbox.psd1 │ │ │ │ ├── msExchangeSGLegacyInterop.json │ │ │ │ ├── msExchangeSGOrgManagement.json │ │ │ │ ├── msExchangeSGPubFolManagement.json │ │ │ │ ├── msExchangeOtherObjects.psd1 │ │ │ │ ├── msExchangeSGExchangeServers.json │ │ │ │ └── msExchangeSecurityGroups.json │ │ │ │ └── objectCategories │ │ │ │ └── msExchangeSystemMailbox.psd1 │ │ └── ExchangeDefaults │ │ │ └── domain │ │ │ └── accessRules │ │ │ ├── msExchangeSGLegacyInterop.json │ │ │ ├── msExchangeSGOrgManagement.json │ │ │ ├── msExchangeSGPubFolManagement.json │ │ │ ├── msExchangeSGExchangeServers.json │ │ │ └── msExchangeSecurityGroups.json │ ├── tepp │ │ ├── components.Tepp.ps1 │ │ ├── assignment.ps1 │ │ ├── context.Tepp.ps1 │ │ ├── credentialProvider.Tepp.ps1 │ │ └── readme.md │ ├── scripts │ │ ├── preimport.ps1 │ │ ├── variables.ps1 │ │ ├── strings.ps1 │ │ ├── license.ps1 │ │ ├── postimport.ps1 │ │ └── initialize.ps1 │ ├── functions │ │ ├── readme.md │ │ ├── Reset-DomainControllerCache.ps1 │ │ ├── Resolve-DataFile.ps1 │ │ ├── Invoke-PostCredentialProvider.ps1 │ │ └── Invoke-PreCredentialProvider.ps1 │ ├── configurations │ │ ├── readme.md │ │ └── configuration.ps1 │ ├── configurationValidation │ │ └── DCSelectionMode.ps1 │ └── scriptblocks │ │ └── scriptblocks.ps1 ├── bin │ ├── ADMF.dll │ ├── ADMF.pdb │ ├── ADMF.xml │ └── readme.md ├── en-us │ ├── about_ADMF.help.txt │ └── about_ADMF_Context.help.txt ├── functions │ ├── readme.md │ ├── Get-AdmfContextStore.ps1 │ ├── New-AdmfContextStore.ps1 │ ├── Register-AdmfCredentialProvider.ps1 │ ├── Test-AdmfDC.ps1 │ ├── Get-AdmfContext.ps1 │ ├── Invoke-AdmfDC.ps1 │ └── Export-AdmfGpo.ps1 ├── tests │ ├── functions │ │ └── readme.md │ ├── general │ │ ├── Help.Exceptions.ps1 │ │ ├── FileIntegrity.Exceptions.ps1 │ │ ├── strings.Tests.ps1 │ │ ├── strings.Exceptions.ps1 │ │ ├── PSScriptAnalyzer.Tests.ps1 │ │ ├── Manifest.Tests.ps1 │ │ └── FileIntegrity.Tests.ps1 │ ├── readme.md │ └── pester.ps1 ├── readme.md ├── xml │ ├── ADMF.Types.ps1xml │ ├── readme.md │ └── ADMF.Format.ps1xml ├── ADMF.psm1 └── ADMF.psd1 ├── azFunctionResources ├── host.json ├── requirements.psd1 ├── host-az.json ├── functionOverride │ ├── Get-Example.psd1 │ ├── Get-Example.json │ └── Get-Example.ps1 ├── local.settings.json ├── clientModule │ ├── function.ps1 │ ├── moduleroot.psm1 │ ├── internal │ │ ├── configurations │ │ │ └── connection.ps1 │ │ └── functions │ │ │ └── Get-InternalConnectionData.ps1 │ └── functions │ │ └── Connect-ADMF.ps1 ├── profile.ps1 ├── run.ps1 ├── readme.md └── profileFunctions │ ├── Convert-AzureFunctionParameter.ps1 │ └── Write-AzureFunctionOutput.ps1 ├── .gitattributes ├── .github ├── workflows │ ├── validate.yml │ └── build.yml └── FUNDING.yml ├── library └── ADMF │ ├── ADMF │ ├── UpdateDCOptions.cs │ ├── ADMF.csproj │ ├── UpdateForestOptions.cs │ └── UpdateDomainOptions.cs │ └── ADMF.sln ├── .gitignore ├── azure-pipelines.yml ├── README.md └── LICENSE /ADMF/internal/data/context/domain/gpowners/readme.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ADMF/internal/data/context/domain/gppermissionfilters/readme.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ADMF/internal/data/domainDefaults/gppermissionfilters/readme.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /azFunctionResources/host.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0" 3 | } -------------------------------------------------------------------------------- /azFunctionResources/requirements.psd1: -------------------------------------------------------------------------------- 1 | @{ 2 | Az = '1.*' 3 | } -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto -------------------------------------------------------------------------------- /ADMF/internal/components/DefaultAccessRules/domain/gppermissionfilters/readme.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ADMF/bin/ADMF.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ActiveDirectoryManagementFramework/ADMF/HEAD/ADMF/bin/ADMF.dll -------------------------------------------------------------------------------- /ADMF/bin/ADMF.pdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ActiveDirectoryManagementFramework/ADMF/HEAD/ADMF/bin/ADMF.pdb -------------------------------------------------------------------------------- /azFunctionResources/host-az.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0", 3 | "managedDependency": { 4 | "Enabled": true 5 | } 6 | } -------------------------------------------------------------------------------- /ADMF/bin/ADMF.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ADMF 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ADMF/internal/tepp/components.Tepp.ps1: -------------------------------------------------------------------------------- 1 | Register-PSFTeppScriptblock -Name 'ADMF.Components' -ScriptBlock { 2 | & (Get-Module ADMF) { (Get-ChildItem -Path "$script:ModuleRoot\internal\components" -Directory).Name } 3 | } -------------------------------------------------------------------------------- /ADMF/en-us/about_ADMF.help.txt: -------------------------------------------------------------------------------- 1 | TOPIC 2 | about_ADMF 3 | 4 | SHORT DESCRIPTION 5 | Explains how to use the ADMF powershell module 6 | 7 | LONG DESCRIPTION 8 | 9 | 10 | KEYWORDS 11 | ADMF -------------------------------------------------------------------------------- /ADMF/internal/scripts/preimport.ps1: -------------------------------------------------------------------------------- 1 | # Add all things you want to run before importing the main code 2 | 3 | # Load the strings used in messages 4 | . Import-ModuleFile -Path "$($script:ModuleRoot)\internal\scripts\strings.ps1" -------------------------------------------------------------------------------- /azFunctionResources/functionOverride/Get-Example.psd1: -------------------------------------------------------------------------------- 1 | @{ 2 | # Override the rest methods used for the API endpoint 3 | # RestMethods = 'delete' 4 | 5 | # Override inclusion into client module 6 | # NoClientFunction 7 | } -------------------------------------------------------------------------------- /azFunctionResources/local.settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "IsEncrypted": false, 3 | "Values": { 4 | "FUNCTIONS_WORKER_RUNTIME": "powershell", 5 | "AzureWebJobsStorage": "--connection string for storage account---" 6 | } 7 | } -------------------------------------------------------------------------------- /ADMF/en-us/about_ADMF_Context.help.txt: -------------------------------------------------------------------------------- 1 | TOPIC 2 | about_ADMF_Context 3 | 4 | SHORT DESCRIPTION 5 | Explains how the context system of ADMF works. 6 | 7 | LONG DESCRIPTION 8 | 9 | 10 | KEYWORDS 11 | ADMF Context -------------------------------------------------------------------------------- /ADMF/functions/readme.md: -------------------------------------------------------------------------------- 1 | # Functions 2 | 3 | This is the folder where the functions go. 4 | 5 | Depending on the complexity of the module, it is recommended to subdivide them into subfolders. 6 | 7 | The module will pick up all .ps1 files recursively -------------------------------------------------------------------------------- /ADMF/internal/tepp/assignment.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | # Example: 3 | Register-PSFTeppArgumentCompleter -Command Get-Alcohol -Parameter Type -Name ADMF.alcohol 4 | #> 5 | Register-PSFTeppArgumentCompleter -Command New-AdmfContext -Parameter Store -Name 'ADMF.Context.Store' -------------------------------------------------------------------------------- /ADMF/tests/functions/readme.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | This is where the function tests go. 4 | 5 | Make sure to put them in folders reflecting the actual module structure. 6 | 7 | It is not necessary to differentiate between internal and public functions here. -------------------------------------------------------------------------------- /ADMF/internal/functions/readme.md: -------------------------------------------------------------------------------- 1 | # Functions 2 | 3 | This is the folder where the internal functions go. 4 | 5 | Depending on the complexity of the module, it is recommended to subdivide them into subfolders. 6 | 7 | The module will pick up all .ps1 files recursively -------------------------------------------------------------------------------- /ADMF/internal/data/domainDefaults/gppermissionfilters/addefault_DefaultDomainPolicy.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "Name": "DefaultDomainPolicy", 4 | "GPName": "^Default Domain Policy$|^Default Domain Controllers Policy$", 5 | "GPNameMode": "Regex" 6 | } 7 | ] -------------------------------------------------------------------------------- /ADMF/internal/components/DefaultAccessRules/domain/gppermissionfilters/addefault_DefaultDomainPolicy.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "Name": "DefaultDomainPolicy", 4 | "GPName": "^Default Domain Policy$|^Default Domain Controllers Policy$", 5 | "GPNameMode": "Regex" 6 | } 7 | ] -------------------------------------------------------------------------------- /ADMF/internal/data/context/postImport.ps1: -------------------------------------------------------------------------------- 1 | # Scriptfile that is executed AFTER the context resources are applied 2 | # Optional, delete file if not needed for performance benefit 3 | 4 | param ( 5 | [PSFComputer] 6 | $Server, 7 | 8 | [System.Management.Automation.PSCredential] 9 | $Credential 10 | ) -------------------------------------------------------------------------------- /ADMF/internal/data/context/preImport.ps1: -------------------------------------------------------------------------------- 1 | # Scriptfile that is executed BEFORE the context resources are applied 2 | # Optional, delete file if not needed for performance benefit 3 | 4 | param ( 5 | [PSFComputer] 6 | $Server, 7 | 8 | [System.Management.Automation.PSCredential] 9 | $Credential 10 | ) -------------------------------------------------------------------------------- /ADMF/internal/tepp/context.Tepp.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | # Example: 3 | Register-PSFTeppScriptblock -Name "ADMF.alcohol" -ScriptBlock { 'Beer','Mead','Whiskey','Wine','Vodka','Rum (3y)', 'Rum (5y)', 'Rum (7y)' } 4 | #> 5 | 6 | Register-PSFTeppScriptblock -Name 'ADMF.Context.Store' -ScriptBlock { 7 | (Get-AdmfContextStore).Name 8 | } -------------------------------------------------------------------------------- /ADMF/internal/data/domainDefaults/objectCategories/addefault_domainControllers.psd1: -------------------------------------------------------------------------------- 1 | @{ 2 | Name = 'DomainController' 3 | ObjectClass = 'computer' 4 | Property = @('PrimaryGroupID') 5 | TestScript = { $args[0].PrimaryGroupID -eq 516 } 6 | LDAPFilter = '(&(objectCategory=computer)(primaryGroupID=516))' 7 | } -------------------------------------------------------------------------------- /ADMF/internal/components/DefaultAccessRules/domain/objectCategories/addefault_domainControllers.psd1: -------------------------------------------------------------------------------- 1 | @{ 2 | Name = 'DomainController' 3 | ObjectClass = 'computer' 4 | Property = @('PrimaryGroupID') 5 | TestScript = { $args[0].PrimaryGroupID -eq 516 } 6 | LDAPFilter = '(&(objectCategory=computer)(primaryGroupID=516))' 7 | } -------------------------------------------------------------------------------- /ADMF/internal/data/domainDefaults/objectCategories/addefault_msDFSR-LocalSettings.psd1: -------------------------------------------------------------------------------- 1 | @{ 2 | Name = 'msDFSR-LocalSettings' 3 | ObjectClass = 'msDFSR-LocalSettings' 4 | Property = @('Name') 5 | TestScript = { $args[0].ObjectClass -eq 'msDFSR-LocalSettings' } 6 | LDAPFilter = '(objectCategory=msDFSR-LocalSettings)' 7 | } -------------------------------------------------------------------------------- /azFunctionResources/clientModule/function.ps1: -------------------------------------------------------------------------------- 1 | function %functionname% 2 | { 3 | %parameter% 4 | 5 | process 6 | { 7 | $invokeParameters = Get-InternalConnectionData -Method '%method%' -Parameter $PSBoundParameters -FunctionName '%condensedname%' 8 | Invoke-RestMethod @invokeParameters | ConvertFrom-PSFClixml 9 | } 10 | } -------------------------------------------------------------------------------- /ADMF/internal/data/domainDefaults/objectCategories/addefault_domainSystemVolume.psd1: -------------------------------------------------------------------------------- 1 | @{ 2 | Name = 'domainSystemVolume' 3 | ObjectClass = 'msDFSR-Subscriber' 4 | Property = @('CN') 5 | TestScript = { $args[0].CN -eq 'Domain System Volume' } 6 | LDAPFilter = '(&(objectCategory=msDFSR-Subscriber)(CN=Domain System Volume))' 7 | } -------------------------------------------------------------------------------- /ADMF/internal/data/domainDefaults/objectCategories/addefault_sysvolSubscription.psd1: -------------------------------------------------------------------------------- 1 | @{ 2 | Name = 'sysvolSubscription' 3 | ObjectClass = 'msDFSR-Subscription' 4 | Property = @('cn') 5 | TestScript = { $args[0].CN -eq 'SYSVOL Subscription' } 6 | LDAPFilter = '(&(objectCategory=serviceConnectionPoint)(cn=SYSVOL Subscription))' 7 | } -------------------------------------------------------------------------------- /ADMF/internal/components/DefaultAccessRules/domain/objectCategories/addefault_msDFSR-LocalSettings.psd1: -------------------------------------------------------------------------------- 1 | @{ 2 | Name = 'msDFSR-LocalSettings' 3 | ObjectClass = 'msDFSR-LocalSettings' 4 | Property = @('Name') 5 | TestScript = { $args[0].ObjectClass -eq 'msDFSR-LocalSettings' } 6 | LDAPFilter = '(objectCategory=msDFSR-LocalSettings)' 7 | } -------------------------------------------------------------------------------- /ADMF/internal/data/domainDefaults/objectCategories/addefault_serviceAccount.psd1: -------------------------------------------------------------------------------- 1 | @{ 2 | Name = 'serviceAccount' 3 | ObjectClass = 'msDS-GroupManagedServiceAccount' 4 | Property = @('Name') 5 | TestScript = { $args[0].ObjectClass -eq 'msDS-GroupManagedServiceAccount' } 6 | LDAPFilter = '(objectCategory=msDS-GroupManagedServiceAccount)' 7 | } -------------------------------------------------------------------------------- /ADMF/internal/components/DefaultAccessRules/domain/objectCategories/addefault_domainSystemVolume.psd1: -------------------------------------------------------------------------------- 1 | @{ 2 | Name = 'domainSystemVolume' 3 | ObjectClass = 'msDFSR-Subscriber' 4 | Property = @('CN') 5 | TestScript = { $args[0].CN -eq 'Domain System Volume' } 6 | LDAPFilter = '(&(objectCategory=msDFSR-Subscriber)(CN=Domain System Volume))' 7 | } -------------------------------------------------------------------------------- /ADMF/internal/data/domainDefaults/objectCategories/addefault_virtualMachines.psd1: -------------------------------------------------------------------------------- 1 | @{ 2 | Name = 'VirtualMachineObject' 3 | ObjectClass = 'serviceConnectionPoint' 4 | Property = @('Name') 5 | TestScript = { $args[0].Name -eq 'Windows Virtual Machine' } 6 | LDAPFilter = '(&(objectCategory=serviceConnectionPoint)(name=Windows Virtual Machine))' 7 | } -------------------------------------------------------------------------------- /ADMF/internal/components/DefaultAccessRules/domain/objectCategories/addefault_sysvolSubscription.psd1: -------------------------------------------------------------------------------- 1 | @{ 2 | Name = 'sysvolSubscription' 3 | ObjectClass = 'msDFSR-Subscription' 4 | Property = @('cn') 5 | TestScript = { $args[0].CN -eq 'SYSVOL Subscription' } 6 | LDAPFilter = '(&(objectCategory=serviceConnectionPoint)(cn=SYSVOL Subscription))' 7 | } -------------------------------------------------------------------------------- /ADMF/internal/components/DefaultAccessRules/domain/objectCategories/addefault_serviceAccount.psd1: -------------------------------------------------------------------------------- 1 | @{ 2 | Name = 'serviceAccount' 3 | ObjectClass = 'msDS-GroupManagedServiceAccount' 4 | Property = @('Name') 5 | TestScript = { $args[0].ObjectClass -eq 'msDS-GroupManagedServiceAccount' } 6 | LDAPFilter = '(objectCategory=msDS-GroupManagedServiceAccount)' 7 | } -------------------------------------------------------------------------------- /ADMF/internal/components/DefaultAccessRules/domain/objectCategories/addefault_virtualMachines.psd1: -------------------------------------------------------------------------------- 1 | @{ 2 | Name = 'VirtualMachineObject' 3 | ObjectClass = 'serviceConnectionPoint' 4 | Property = @('Name') 5 | TestScript = { $args[0].Name -eq 'Windows Virtual Machine' } 6 | LDAPFilter = '(&(objectCategory=serviceConnectionPoint)(name=Windows Virtual Machine))' 7 | } -------------------------------------------------------------------------------- /ADMF/internal/scripts/variables.ps1: -------------------------------------------------------------------------------- 1 | # The list of currently applied context sets 2 | $script:loadedContexts = @() 3 | 4 | # The list of contexts per domain/server 5 | $script:assignedContexts = @{ } 6 | 7 | # The list of registered credentials providers 8 | $script:credentialProviders = @{ } 9 | 10 | # Currently resolved domain controller 11 | $script:resolvedDomainController = $null -------------------------------------------------------------------------------- /.github/workflows/validate.yml: -------------------------------------------------------------------------------- 1 | on: [pull_request] 2 | 3 | jobs: 4 | validate: 5 | 6 | runs-on: windows-latest 7 | 8 | steps: 9 | - uses: actions/checkout@v1 10 | - name: Install Prerequisites 11 | run: .\build\vsts-prerequisites.ps1 12 | shell: powershell 13 | - name: Validate 14 | run: .\build\vsts-validate.ps1 15 | shell: powershell 16 | -------------------------------------------------------------------------------- /ADMF/bin/readme.md: -------------------------------------------------------------------------------- 1 | # bin folder 2 | 3 | The bin folder exists to store binary data. And scripts related to the type system. 4 | 5 | This may include your own C#-based library, third party libraries you want to include (watch the license!), or a script declaring type accelerators (effectively aliases for .NET types) 6 | 7 | For more information on Type Accelerators, see the help on Set-PSFTypeAlias -------------------------------------------------------------------------------- /ADMF/internal/data/domainDefaults/objectCategories/addefault_systemDomainUpdates.psd1: -------------------------------------------------------------------------------- 1 | @{ 2 | Name = 'systemDomainUpdates' 3 | ObjectClass = 'container' 4 | Property = 'DistinguishedName' 5 | TestScript = { $args[0].DistinguishedName -like '*,CN=DomainUpdates,CN=System,DC=*' } 6 | LDAPFilter = '(objectCategory=container)' 7 | SearchBase = 'CN=DomainUpdates,CN=System,%DomainDN%' 8 | } -------------------------------------------------------------------------------- /ADMF/internal/data/domainDefaults/acls/addefault_default.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "Owner": "%DomainSID%-512", 4 | "DefaultOwner": true 5 | }, 6 | { 7 | "Owner": "S-1-5-32-544", 8 | "Path": "CN=BuiltIn,%DomainDN%" 9 | }, 10 | { 11 | "Owner": "%DomainSID%-512", 12 | "Path": "CN=Keys,%DomainDN%", 13 | "NoInheritance": true 14 | } 15 | ] -------------------------------------------------------------------------------- /ADMF/internal/data/domainDefaults/accessRules/addefault_virtualMachines.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "ObjectCategory": "VirtualMachineObject", 4 | "ActiveDirectoryRights": "GenericAll", 5 | "InheritanceType": "None", 6 | "ObjectType": "", 7 | "InheritedObjectType": "", 8 | "AccessControlType": "Allow", 9 | "Identity": "" 10 | } 11 | ] -------------------------------------------------------------------------------- /ADMF/internal/components/DefaultAccessRules/domain/objectCategories/addefault_systemDomainUpdates.psd1: -------------------------------------------------------------------------------- 1 | @{ 2 | Name = 'systemDomainUpdates' 3 | ObjectClass = 'container' 4 | Property = 'DistinguishedName' 5 | TestScript = { $args[0].DistinguishedName -like '*,CN=DomainUpdates,CN=System,DC=*' } 6 | LDAPFilter = '(objectCategory=container)' 7 | SearchBase = 'CN=DomainUpdates,CN=System,%DomainDN%' 8 | } -------------------------------------------------------------------------------- /ADMF/internal/data/exchangeSPDefaults/accessRules/msExchangeSystemMailbox.psd1: -------------------------------------------------------------------------------- 1 | @{ 2 | ObjectCategory = 'msExchangeSystemMailbox' 3 | Identity = '%DomainNetBIOSName%\Organization Management' 4 | ActiveDirectoryRights = 'GenericAll' 5 | InheritanceType = 'All' 6 | AccessControlType = 'Allow' 7 | ObjectType = '' 8 | InheritedObjectType = '' 9 | } -------------------------------------------------------------------------------- /ADMF/internal/components/DefaultAccessRules/domain/acls/addefault_default.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "Owner": "%DomainSID%-512", 4 | "DefaultOwner": true 5 | }, 6 | { 7 | "Owner": "S-1-5-32-544", 8 | "Path": "CN=BuiltIn,%DomainDN%" 9 | }, 10 | { 11 | "Owner": "%DomainSID%-512", 12 | "Path": "CN=Keys,%DomainDN%", 13 | "NoInheritance": true 14 | } 15 | ] -------------------------------------------------------------------------------- /ADMF/internal/components/DefaultAccessRules/domain/accessRules/addefault_virtualMachines.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "ObjectCategory": "VirtualMachineObject", 4 | "ActiveDirectoryRights": "GenericAll", 5 | "InheritanceType": "None", 6 | "ObjectType": "", 7 | "InheritedObjectType": "", 8 | "AccessControlType": "Allow", 9 | "Identity": "" 10 | } 11 | ] -------------------------------------------------------------------------------- /ADMF/internal/components/ExchangeSPDefaults/domain/accessRules/msExchangeSystemMailbox.psd1: -------------------------------------------------------------------------------- 1 | @{ 2 | ObjectCategory = 'msExchangeSystemMailbox' 3 | Identity = '%DomainNetBIOSName%\Organization Management' 4 | ActiveDirectoryRights = 'GenericAll' 5 | InheritanceType = 'All' 6 | AccessControlType = 'Allow' 7 | ObjectType = '' 8 | InheritedObjectType = '' 9 | } -------------------------------------------------------------------------------- /library/ADMF/ADMF/UpdateDCOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace ADMF 8 | { 9 | [Flags] 10 | public enum UpdateDCOptions 11 | { 12 | Share = 1, 13 | FSAccessRule = 2, 14 | 15 | Default = Share | FSAccessRule, 16 | All = Share | FSAccessRule 17 | } 18 | } -------------------------------------------------------------------------------- /ADMF/internal/data/exchangeDefaults/accessRules/msExchangeSGLegacyInterop.json: -------------------------------------------------------------------------------- 1 | { 2 | "Path": "CN=ExchangeLegacyInterop,OU=Microsoft Exchange Security Groups,%DomainDN%", 3 | "ActiveDirectoryRights": "GenericAll", 4 | "InheritanceType": "All", 5 | "ObjectType": "", 6 | "InheritedObjectType": "", 7 | "AccessControlType": "Allow", 8 | "Identity": "%DomainNetBIOSName%\\Organization Management" 9 | } -------------------------------------------------------------------------------- /ADMF/internal/data/exchangeDefaults/accessRules/msExchangeSGOrgManagement.json: -------------------------------------------------------------------------------- 1 | { 2 | "Path": "CN=Organization Management,OU=Microsoft Exchange Security Groups,%DomainDN%", 3 | "ActiveDirectoryRights": "GenericAll", 4 | "InheritanceType": "All", 5 | "ObjectType": "", 6 | "InheritedObjectType": "", 7 | "AccessControlType": "Allow", 8 | "Identity": "%DomainNetBIOSName%\\Organization Management" 9 | } -------------------------------------------------------------------------------- /ADMF/internal/data/exchangeDefaults/accessRules/msExchangeSGPubFolManagement.json: -------------------------------------------------------------------------------- 1 | { 2 | "Path": "CN=Public Folder Management,OU=Microsoft Exchange Security Groups,%DomainDN%", 3 | "ActiveDirectoryRights": "GenericAll", 4 | "InheritanceType": "All", 5 | "ObjectType": "", 6 | "InheritedObjectType": "", 7 | "AccessControlType": "Allow", 8 | "Identity": "%DomainNetBIOSName%\\Organization Management" 9 | } -------------------------------------------------------------------------------- /ADMF/internal/data/exchangeSPDefaults/accessRules/msExchangeSGLegacyInterop.json: -------------------------------------------------------------------------------- 1 | { 2 | "Path": "CN=ExchangeLegacyInterop,OU=Microsoft Exchange Security Groups,%DomainDN%", 3 | "ActiveDirectoryRights": "GenericAll", 4 | "InheritanceType": "All", 5 | "ObjectType": "", 6 | "InheritedObjectType": "", 7 | "AccessControlType": "Allow", 8 | "Identity": "%DomainNetBIOSName%\\Organization Management" 9 | } -------------------------------------------------------------------------------- /ADMF/internal/data/exchangeSPDefaults/accessRules/msExchangeSGOrgManagement.json: -------------------------------------------------------------------------------- 1 | { 2 | "Path": "CN=Organization Management,OU=Microsoft Exchange Security Groups,%DomainDN%", 3 | "ActiveDirectoryRights": "GenericAll", 4 | "InheritanceType": "All", 5 | "ObjectType": "", 6 | "InheritedObjectType": "", 7 | "AccessControlType": "Allow", 8 | "Identity": "%DomainNetBIOSName%\\Organization Management" 9 | } -------------------------------------------------------------------------------- /ADMF/internal/data/exchangeSPDefaults/accessRules/msExchangeSGPubFolManagement.json: -------------------------------------------------------------------------------- 1 | { 2 | "Path": "CN=Public Folder Management,OU=Microsoft Exchange Security Groups,%DomainDN%", 3 | "ActiveDirectoryRights": "GenericAll", 4 | "InheritanceType": "All", 5 | "ObjectType": "", 6 | "InheritedObjectType": "", 7 | "AccessControlType": "Allow", 8 | "Identity": "%DomainNetBIOSName%\\Organization Management" 9 | } -------------------------------------------------------------------------------- /ADMF/internal/components/ExchangeDefaults/domain/accessRules/msExchangeSGLegacyInterop.json: -------------------------------------------------------------------------------- 1 | { 2 | "Path": "CN=ExchangeLegacyInterop,OU=Microsoft Exchange Security Groups,%DomainDN%", 3 | "ActiveDirectoryRights": "GenericAll", 4 | "InheritanceType": "All", 5 | "ObjectType": "", 6 | "InheritedObjectType": "", 7 | "AccessControlType": "Allow", 8 | "Identity": "%DomainNetBIOSName%\\Organization Management" 9 | } -------------------------------------------------------------------------------- /ADMF/internal/components/ExchangeDefaults/domain/accessRules/msExchangeSGOrgManagement.json: -------------------------------------------------------------------------------- 1 | { 2 | "Path": "CN=Organization Management,OU=Microsoft Exchange Security Groups,%DomainDN%", 3 | "ActiveDirectoryRights": "GenericAll", 4 | "InheritanceType": "All", 5 | "ObjectType": "", 6 | "InheritedObjectType": "", 7 | "AccessControlType": "Allow", 8 | "Identity": "%DomainNetBIOSName%\\Organization Management" 9 | } -------------------------------------------------------------------------------- /ADMF/internal/components/ExchangeSPDefaults/domain/accessRules/msExchangeSGLegacyInterop.json: -------------------------------------------------------------------------------- 1 | { 2 | "Path": "CN=ExchangeLegacyInterop,OU=Microsoft Exchange Security Groups,%DomainDN%", 3 | "ActiveDirectoryRights": "GenericAll", 4 | "InheritanceType": "All", 5 | "ObjectType": "", 6 | "InheritedObjectType": "", 7 | "AccessControlType": "Allow", 8 | "Identity": "%DomainNetBIOSName%\\Organization Management" 9 | } -------------------------------------------------------------------------------- /ADMF/internal/components/ExchangeDefaults/domain/accessRules/msExchangeSGPubFolManagement.json: -------------------------------------------------------------------------------- 1 | { 2 | "Path": "CN=Public Folder Management,OU=Microsoft Exchange Security Groups,%DomainDN%", 3 | "ActiveDirectoryRights": "GenericAll", 4 | "InheritanceType": "All", 5 | "ObjectType": "", 6 | "InheritedObjectType": "", 7 | "AccessControlType": "Allow", 8 | "Identity": "%DomainNetBIOSName%\\Organization Management" 9 | } -------------------------------------------------------------------------------- /ADMF/internal/components/ExchangeSPDefaults/domain/accessRules/msExchangeSGOrgManagement.json: -------------------------------------------------------------------------------- 1 | { 2 | "Path": "CN=Organization Management,OU=Microsoft Exchange Security Groups,%DomainDN%", 3 | "ActiveDirectoryRights": "GenericAll", 4 | "InheritanceType": "All", 5 | "ObjectType": "", 6 | "InheritedObjectType": "", 7 | "AccessControlType": "Allow", 8 | "Identity": "%DomainNetBIOSName%\\Organization Management" 9 | } -------------------------------------------------------------------------------- /ADMF/internal/components/ExchangeSPDefaults/domain/accessRules/msExchangeSGPubFolManagement.json: -------------------------------------------------------------------------------- 1 | { 2 | "Path": "CN=Public Folder Management,OU=Microsoft Exchange Security Groups,%DomainDN%", 3 | "ActiveDirectoryRights": "GenericAll", 4 | "InheritanceType": "All", 5 | "ObjectType": "", 6 | "InheritedObjectType": "", 7 | "AccessControlType": "Allow", 8 | "Identity": "%DomainNetBIOSName%\\Organization Management" 9 | } -------------------------------------------------------------------------------- /azFunctionResources/profile.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | This is the globl profile file for the Azure Function App. 3 | This file will have been executed first, before any function runs. 4 | Use this to create a common execution environment, 5 | but keep in mind that the profile execution time is added to the function startup time for ALL functions. 6 | #> 7 | 8 | if ($env:MSI_SECRET -and (Get-Module -ListAvailable Az.Accounts)) 9 | { 10 | Connect-AzAccount -Identity 11 | } -------------------------------------------------------------------------------- /azFunctionResources/functionOverride/Get-Example.json: -------------------------------------------------------------------------------- 1 | { 2 | "bindings": [ 3 | { 4 | "authLevel": "function", 5 | "type": "httpTrigger", 6 | "direction": "in", 7 | "name": "Request", 8 | "methods": [ 9 | "delete" 10 | ] 11 | }, 12 | { 13 | "type": "http", 14 | "direction": "out", 15 | "name": "Response" 16 | } 17 | ], 18 | "disabled": true 19 | } -------------------------------------------------------------------------------- /azFunctionResources/run.ps1: -------------------------------------------------------------------------------- 1 | param ( 2 | $Request, 3 | 4 | $TriggerMetadata 5 | ) 6 | 7 | $parameterObject = Convert-AzureFunctionParameter -Request $Request 8 | $parameters = $parameterObject.Parameters 9 | try { $data = %functionname% @parameters } 10 | catch 11 | { 12 | Write-AzureFunctionOutput -Value "Failed to execute: $_" -Status InternalServerError 13 | return 14 | } 15 | 16 | Write-AzureFunctionOutput -Value $data -Serialize:$parameterObject.Serialize -------------------------------------------------------------------------------- /ADMF/internal/scripts/strings.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | This file loads the strings documents from the respective language folders. 3 | This allows localizing messages and errors. 4 | Load psd1 language files for each language you wish to support. 5 | Partial translations are acceptable - when missing a current language message, 6 | it will fallback to English or another available language. 7 | #> 8 | Import-PSFLocalizedString -Path "$($script:ModuleRoot)\en-us\*.psd1" -Module 'ADMF' -Language 'en-US' -------------------------------------------------------------------------------- /ADMF/internal/data/context/domain/acls/readme.md: -------------------------------------------------------------------------------- 1 | # Acls 2 | 3 | ## Synopsis 4 | 5 | Add configuration data to manage acls 6 | 7 | Place any number of json files in this folder. 8 | Each file should contain an array of objects. 9 | What properties to place on each object depends on the parameters present on the relevant 'Register-*'-Command. 10 | For a full description of the supported properties, see the parameter descriptions: 11 | 12 | Get-Help Register-DMAcl -Detailed 13 | 14 | -------------------------------------------------------------------------------- /ADMF/internal/data/context/domain/users/readme.md: -------------------------------------------------------------------------------- 1 | # Users 2 | 3 | ## Synopsis 4 | 5 | Add configuration data to manage users 6 | 7 | Place any number of json files in this folder. 8 | Each file should contain an array of objects. 9 | What properties to place on each object depends on the parameters present on the relevant 'Register-*'-Command. 10 | For a full description of the supported properties, see the parameter descriptions: 11 | 12 | Get-Help Register-DMUser -Detailed 13 | -------------------------------------------------------------------------------- /ADMF/internal/data/context/forest/sites/readme.md: -------------------------------------------------------------------------------- 1 | # Sites 2 | 3 | ## Synopsis 4 | 5 | Add configuration data to manage sites 6 | 7 | Place any number of json files in this folder. 8 | Each file should contain an array of objects. 9 | What properties to place on each object depends on the parameters present on the relevant 'Register-*'-Command. 10 | For a full description of the supported properties, see the parameter descriptions: 11 | 12 | Get-Help Register-FMSite -Detailed 13 | -------------------------------------------------------------------------------- /ADMF/internal/data/context/domain/gplinks/readme.md: -------------------------------------------------------------------------------- 1 | # gplinks 2 | 3 | ## Synopsis 4 | 5 | Add configuration data to manage gplinks 6 | 7 | Place any number of json files in this folder. 8 | Each file should contain an array of objects. 9 | What properties to place on each object depends on the parameters present on the relevant 'Register-*'-Command. 10 | For a full description of the supported properties, see the parameter descriptions: 11 | 12 | Get-Help Register-DMGPLink -Detailed 13 | -------------------------------------------------------------------------------- /ADMF/internal/data/context/forest/subnets/readme.md: -------------------------------------------------------------------------------- 1 | # Subnets 2 | 3 | ## Synopsis 4 | 5 | Add configuration data to manage subnets 6 | 7 | Place any number of json files in this folder. 8 | Each file should contain an array of objects. 9 | What properties to place on each object depends on the parameters present on the relevant 'Register-*'-Command. 10 | For a full description of the supported properties, see the parameter descriptions: 11 | 12 | Get-Help Register-FMSubnet -Detailed 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # ignore the settings folder and files for VSCode and PSS 3 | .vscode/* 4 | *.psproj 5 | *TempPoint* 6 | 7 | # Ignore staging info from Visual Studio 8 | library/ADMF/.vs/* 9 | library/ADMF/ADMF/bin/* 10 | library/ADMF/ADMF/obj/* 11 | 12 | # ignore PowerShell Studio MetaData 13 | ADMF/ADMF.psproj 14 | ADMF/ADMF.psproj.bak 15 | ADMF/ADMF.psprojs 16 | ADMF/ADMF.psproj 17 | 18 | # ignore the TestResults 19 | TestResults/* 20 | 21 | # ignore the publishing Directory 22 | publish/* -------------------------------------------------------------------------------- /ADMF/internal/data/context/domain/groups/readme.md: -------------------------------------------------------------------------------- 1 | # groups 2 | 3 | ## Synopsis 4 | 5 | Add configuration data to manage groups 6 | 7 | Place any number of json files in this folder. 8 | Each file should contain an array of objects. 9 | What properties to place on each object depends on the parameters present on the relevant 'Register-*'-Command. 10 | For a full description of the supported properties, see the parameter descriptions: 11 | 12 | Get-Help Register-DMGroup -Detailed 13 | 14 | -------------------------------------------------------------------------------- /ADMF/internal/data/context/forest/sitelinks/readme.md: -------------------------------------------------------------------------------- 1 | # Sitelinks 2 | 3 | ## Synopsis 4 | 5 | Add configuration data to manage sitelinks 6 | 7 | Place any number of json files in this folder. 8 | Each file should contain an array of objects. 9 | What properties to place on each object depends on the parameters present on the relevant 'Register-*'-Command. 10 | For a full description of the supported properties, see the parameter descriptions: 11 | 12 | Get-Help Register-FMSiteLink -Detailed 13 | -------------------------------------------------------------------------------- /ADMF/internal/data/context/forest/schema/readme.md: -------------------------------------------------------------------------------- 1 | # Schema 2 | 3 | ## Synopsis 4 | 5 | Add configuration data to manage custom schema extensions 6 | 7 | Place any number of json files in this folder. 8 | Each file should contain an array of objects. 9 | What properties to place on each object depends on the parameters present on the relevant 'Register-*'-Command. 10 | For a full description of the supported properties, see the parameter descriptions: 11 | 12 | Get-Help Register-FMSchema -Detailed 13 | -------------------------------------------------------------------------------- /ADMF/internal/data/context/domain/accessrules/readme.md: -------------------------------------------------------------------------------- 1 | # accessrules 2 | 3 | ## Synopsis 4 | 5 | Add configuration data to manage accessrules 6 | 7 | Place any number of json files in this folder. 8 | Each file should contain an array of objects. 9 | What properties to place on each object depends on the parameters present on the relevant 'Register-*'-Command. 10 | For a full description of the supported properties, see the parameter descriptions: 11 | 12 | Get-Help Register-DMAccessRule -Detailed 13 | 14 | -------------------------------------------------------------------------------- /azFunctionResources/clientModule/moduleroot.psm1: -------------------------------------------------------------------------------- 1 | $script:ModuleRoot = $PSScriptRoot 2 | 3 | foreach ($file in (Get-ChildItem -Path "$script:ModuleRoot\internal\configurations" -Recurse -Filter '*.ps1')) 4 | { 5 | . $file.FullName 6 | } 7 | foreach ($file in (Get-ChildItem -Path "$script:ModuleRoot\internal\functions" -Recurse -Filter '*.ps1')) 8 | { 9 | . $file.FullName 10 | } 11 | foreach ($file in (Get-ChildItem -Path "$script:ModuleRoot\functions" -Recurse -Filter '*.ps1')) 12 | { 13 | . $file.FullName 14 | } -------------------------------------------------------------------------------- /ADMF/internal/data/context/domain/builtinsids/readme.md: -------------------------------------------------------------------------------- 1 | # BuiltIn SIDs 2 | 3 | ## Synopsis 4 | 5 | Add mapping data, to map localized versions of BuiltIn SIDs. 6 | 7 | Place any number of json files in this folder. 8 | Each file should contain an array of objects. 9 | What properties to place on each object depends on the parameters present on the relevant 'Register-*'-Command. 10 | For a full description of the supported properties, see the parameter descriptions: 11 | 12 | Get-Help Register-DMBuiltInSID -Detailed 13 | -------------------------------------------------------------------------------- /ADMF/internal/data/context/domain/psos/readme.md: -------------------------------------------------------------------------------- 1 | # Finegrained Password Policy objects 2 | 3 | ## Synopsis 4 | 5 | Add configuration data to manage psos 6 | 7 | Place any number of json files in this folder. 8 | Each file should contain an array of objects. 9 | What properties to place on each object depends on the parameters present on the relevant 'Register-*'-Command. 10 | For a full description of the supported properties, see the parameter descriptions: 11 | 12 | Get-Help Register-DMPasswordPolicy -Detailed 13 | 14 | -------------------------------------------------------------------------------- /ADMF/internal/data/context/domain/groupmemberships/readme.md: -------------------------------------------------------------------------------- 1 | # Group Memberships 2 | 3 | ## Synopsis 4 | 5 | Add configuration data to manage group memberships 6 | 7 | Place any number of json files in this folder. 8 | Each file should contain an array of objects. 9 | What properties to place on each object depends on the parameters present on the relevant 'Register-*'-Command. 10 | For a full description of the supported properties, see the parameter descriptions: 11 | 12 | Get-Help Register-DMGroupMembership -Detailed 13 | -------------------------------------------------------------------------------- /azure-pipelines.yml: -------------------------------------------------------------------------------- 1 | pool: 2 | name: Hosted VS2017 3 | steps: 4 | - task: PowerShell@2 5 | displayName: Prerequisites 6 | inputs: 7 | targetType: filePath 8 | filePath: './build/vsts-prerequisites.ps1' 9 | 10 | - task: PowerShell@2 11 | displayName: Validate 12 | inputs: 13 | targetType: filePath 14 | filePath: './build/vsts-validate.ps1' 15 | 16 | - task: PublishTestResults@2 17 | displayName: 'Publish Test Results **/TEST-*.xml' 18 | inputs: 19 | testResultsFormat: NUnit 20 | condition: always() 21 | -------------------------------------------------------------------------------- /ADMF/internal/data/context/domain/organizationalunits/readme.md: -------------------------------------------------------------------------------- 1 | # Organizational Units 2 | 3 | ## Synopsis 4 | 5 | Add configuration data to manage organizational units 6 | 7 | Place any number of json files in this folder. 8 | Each file should contain an array of objects. 9 | What properties to place on each object depends on the parameters present on the relevant 'Register-*'-Command. 10 | For a full description of the supported properties, see the parameter descriptions: 11 | 12 | Get-Help Register-DMOrganizationalUnit -Detailed 13 | 14 | -------------------------------------------------------------------------------- /azFunctionResources/readme.md: -------------------------------------------------------------------------------- 1 | # Azure Function Resources 2 | 3 | This folder is used to store Azure Function specific meta data and resources. 4 | 5 | This folder is also used to allow the user to easily create a custom function-specific configuration, for exanmple in order to change the trigger settings. 6 | 7 | To specify custom, 'Per Function' configuration json, just place the desired configuration file as 'functionname.json' into this folder (it does not matter if it is the PowerShell function name or the condensed version used for publishing on Azure). -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: 4 | - master 5 | 6 | jobs: 7 | build: 8 | 9 | runs-on: windows-latest 10 | 11 | steps: 12 | - uses: actions/checkout@v1 13 | - name: Install Prerequisites 14 | run: .\build\vsts-prerequisites.ps1 15 | shell: powershell 16 | - name: Validate 17 | run: .\build\vsts-validate.ps1 18 | shell: powershell 19 | - name: Build 20 | run: .\build\vsts-build.ps1 -ApiKey $env:APIKEY 21 | shell: powershell 22 | env: 23 | APIKEY: ${{ secrets.ApiKey }} 24 | -------------------------------------------------------------------------------- /ADMF/internal/configurations/readme.md: -------------------------------------------------------------------------------- 1 | # Configurations 2 | 3 | Through the `PSFramework` you have a simple method that allows you to ... 4 | 5 | - Publish settings 6 | - With onboard documentation 7 | - Input validation 8 | - Scripts that run on change of settings 9 | - That can be discovered and updated by the user 10 | - That can be administrated by policy & DSC 11 | 12 | The configuration system is a bit too complex to describe in a help file, you can however visit us at http://psframework.org for detailed guidance. 13 | 14 | An example can be seen in the attached ps1 file -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Active Directory Management Framework 2 | 3 | ## Synopsis 4 | 5 | Central management module to orchestrate configurations and utilize the various management components that make up the Active Directory Management Framework 6 | 7 | + For more details, see the [Project Website](https://admf.one/) 8 | + To jump right in, see our [Quick Start Guide](https://admf.one/documentation/basics/getting-started.html) 9 | + For the Component reference, the definition for each definable aspect, [look no further than here](https://admf.one/documentation/components/components.html) 10 | -------------------------------------------------------------------------------- /ADMF/internal/data/exchangeSPDefaults/objectCategories/msExchangeSystemMailbox.psd1: -------------------------------------------------------------------------------- 1 | @{ 2 | Name = 'msExchangeSystemMailbox' 3 | ObjectClass = 'user' 4 | Property = 'DistinguishedName' 5 | TestScript = { $args[0].DistinguishedName -match '^CN=(SystemMailbox|DiscoverySearchMailbox |Migration\.|FederatedEmail\.)(\{{0,1}([0-9a-fA-F]){8}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){12}\}{0,1}),CN=Users,DC=' } 6 | LDAPFilter = '(&(objectCategory=user)(|(name=SystemMailbox*)(name=DiscoverySearchMailbox*|Migration.*|FederatedEmail.*)))' 7 | SearchBase = 'CN=Users,%DomainDN%' 8 | } -------------------------------------------------------------------------------- /ADMF/internal/components/ExchangeSPDefaults/domain/objectCategories/msExchangeSystemMailbox.psd1: -------------------------------------------------------------------------------- 1 | @{ 2 | Name = 'msExchangeSystemMailbox' 3 | ObjectClass = 'user' 4 | Property = 'DistinguishedName' 5 | TestScript = { $args[0].DistinguishedName -match '^CN=(SystemMailbox|DiscoverySearchMailbox |Migration\.|FederatedEmail\.)(\{{0,1}([0-9a-fA-F]){8}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){12}\}{0,1}),CN=Users,DC=' } 6 | LDAPFilter = '(&(objectCategory=user)(|(name=SystemMailbox*)(name=DiscoverySearchMailbox*|Migration.*|FederatedEmail.*)))' 7 | SearchBase = 'CN=Users,%DomainDN%' 8 | } -------------------------------------------------------------------------------- /ADMF/internal/configurationValidation/DCSelectionMode.ps1: -------------------------------------------------------------------------------- 1 | Register-PSFConfigValidation -Name "DCSelectionMode" -ScriptBlock { 2 | Param ( 3 | $Value 4 | ) 5 | 6 | $Result = New-Object PSObject -Property @{ 7 | Success = $True 8 | Value = $null 9 | Message = "" 10 | } 11 | $legalModes = @( 12 | 'Random' 13 | 'PDCEmulator' 14 | 'Site' 15 | ) 16 | 17 | if ($Value -notin $legalModes) { 18 | $Result.Message = "Bad value: $Value is not any of '$($legalModes -join ",")'" 19 | $Result.Success = $False 20 | return $Result 21 | } 22 | 23 | $Result.Value = $Value -as [string] 24 | 25 | return $Result 26 | } -------------------------------------------------------------------------------- /ADMF/internal/data/context/forest/servers/readme.md: -------------------------------------------------------------------------------- 1 | # Servers 2 | 3 | The servers Component deals with moving domain controllers to the sites they are supposed to be at, based off IPAddress and site subnets. 4 | As such, there is generally not much need to configure this component. 5 | 6 | That said, there is however one setting that can be set: 7 | To disable it. 8 | 9 | Add a json file with this content to disable automatically assigning servers to sites: 10 | 11 | ```json 12 | { 13 | "NoAutoAssignment": true 14 | } 15 | ``` 16 | 17 | With that it is now possible to disable this Component at the configuration level, where previously you needed to watch your Invoke-AdmfForest options. 18 | -------------------------------------------------------------------------------- /ADMF/internal/data/context/domain/grouppolicies/readme.md: -------------------------------------------------------------------------------- 1 | # Group Policy Objects 2 | 3 | ## Synopsis 4 | 5 | Define group policy objects to be applied. 6 | 7 | Use Export-AdmfGpo to create an export of group policies to apply. 8 | These should be copied in their entirety, including the generated exportData.json. 9 | The system uses this exportData.json file to register the correct metadata, so getting that right kind of matters. 10 | If you later want to update only a single gpo, export just that GPO into a new file, replace the GPO folder and _update_ the ExportID in exportData.json to the new value from the new export. 11 | 12 | It is using that ExportID that the system will recognize the need to update a GPO! 13 | -------------------------------------------------------------------------------- /azFunctionResources/clientModule/internal/configurations/connection.ps1: -------------------------------------------------------------------------------- 1 | Set-PSFConfig -Module 'ADMF' -Name 'Client.Uri' -Value $null -Initialize -Validation 'string' -Description "Url to connect to the ADMF Azure function" 2 | Set-PSFConfig -Module 'ADMF' -Name 'Client.UnprotectedToken' -Value '' -Initialize -Validation 'string' -Description "The unencrypted access token to the ADMF Azure function. ONLY use this from secure locations or non-sensitive functions!" 3 | Set-PSFConfig -Module 'ADMF' -Name 'Client.ProtectedToken' -Value $null -Initialize -Validation 'credential' -Description "An encrypted access token to the ADMF Azure function. Use this to persist an access token in a way only the current user on the current system can access." -------------------------------------------------------------------------------- /ADMF/internal/data/context/domain/objects/readme.md: -------------------------------------------------------------------------------- 1 | # Objects 2 | 3 | ## Synopsis 4 | 5 | Add configuration data to manage generic ad objects 6 | 7 | > Note: This category allows you to freely define custom AD objects of any kind. However, in opposite to the "managed" object types, this will not remove all undesired objects of its type within the management scope of the domain. 8 | 9 | Place any number of json files in this folder. 10 | Each file should contain an array of objects. 11 | What properties to place on each object depends on the parameters present on the relevant 'Register-*'-Command. 12 | For a full description of the supported properties, see the parameter descriptions: 13 | 14 | Get-Help Register-DMObject -Detailed 15 | 16 | -------------------------------------------------------------------------------- /ADMF/internal/data/domainDefaults/acls/addefault_domaincontroller.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "ObjectCategory": "msDFSR-LocalSettings", 4 | "Owner": "S-1-5-18", 5 | "SearchBase": "OU=Domain Controllers,%DomainDN%" 6 | }, 7 | { 8 | "ObjectCategory": "VirtualMachineObject", 9 | "Owner": "S-1-5-18", 10 | "SearchBase": "OU=Domain Controllers,%DomainDN%" 11 | }, 12 | { 13 | "ObjectCategory": "sysvolSubscription", 14 | "Owner": "S-1-5-18", 15 | "SearchBase": "OU=Domain Controllers,%DomainDN%" 16 | }, 17 | { 18 | "ObjectCategory": "domainSystemVolume", 19 | "Owner": "S-1-5-18", 20 | "SearchBase": "OU=Domain Controllers,%DomainDN%" 21 | } 22 | ] -------------------------------------------------------------------------------- /ADMF/internal/components/DefaultAccessRules/domain/acls/addefault_domaincontroller.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "ObjectCategory": "msDFSR-LocalSettings", 4 | "Owner": "S-1-5-18", 5 | "SearchBase": "OU=Domain Controllers,%DomainDN%" 6 | }, 7 | { 8 | "ObjectCategory": "VirtualMachineObject", 9 | "Owner": "S-1-5-18", 10 | "SearchBase": "OU=Domain Controllers,%DomainDN%" 11 | }, 12 | { 13 | "ObjectCategory": "sysvolSubscription", 14 | "Owner": "S-1-5-18", 15 | "SearchBase": "OU=Domain Controllers,%DomainDN%" 16 | }, 17 | { 18 | "ObjectCategory": "domainSystemVolume", 19 | "Owner": "S-1-5-18", 20 | "SearchBase": "OU=Domain Controllers,%DomainDN%" 21 | } 22 | ] -------------------------------------------------------------------------------- /ADMF/internal/data/context/forest/schemaldif/readme.md: -------------------------------------------------------------------------------- 1 | # Schema (ldif file) 2 | 3 | ## Synopsis 4 | 5 | Sometimes it becomes convenient to roll out schema extensions using an Ldif file. 6 | For example, several Microsoft products require such an extension (e.g. Skype for Business or SCCM). 7 | 8 | While those extension scripts could be converted into regular schema extension configs, *this is often not supported!!!* . 9 | This makes applying the ldif file mandatory to staying in support. 10 | 11 | Simply drop the relevant ldif files in this folder, using a file-name that represents what they contain. 12 | The Forest Management module will parse them and be able to determine in most cases, whether they have already been applied (and in the correct version). 13 | -------------------------------------------------------------------------------- /ADMF/internal/functions/Reset-DomainControllerCache.ps1: -------------------------------------------------------------------------------- 1 | function Reset-DomainControllerCache { 2 | <# 3 | .SYNOPSIS 4 | Resets the cached domain controller resolution. 5 | 6 | .DESCRIPTION 7 | Resets the cached domain controller resolution. 8 | The targeted domain controller is being cached throughout the execution of a single test or invoke to avoid targeting issues between credential providers and the actual execution. 9 | 10 | .EXAMPLE 11 | PS C:\> Reset-DomainControllerCache 12 | 13 | Resets the cached domain controller resolution. 14 | #> 15 | [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '')] 16 | [CmdletBinding()] 17 | param ( 18 | 19 | ) 20 | 21 | $script:resolvedDomainController = $null 22 | } -------------------------------------------------------------------------------- /library/ADMF/ADMF/ADMF.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net4.5.2 5 | 6 | 7 | 8 | ..\..\..\ADMF\bin 9 | ..\..\..\ADMF\bin\ADMF.xml 10 | 11 | 12 | 13 | ..\..\..\ADMF\bin 14 | ..\..\..\ADMF\bin\ADMF.xml 15 | 16 | 17 | 18 | false 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | FriedrichWeinmann 5 | patreon: # Replace with a single Patreon username 6 | open_collective: # Replace with a single Open Collective username 7 | ko_fi: # Replace with a single Ko-fi username 8 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 9 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 10 | liberapay: # Replace with a single Liberapay username 11 | issuehunt: # Replace with a single IssueHunt username 12 | otechie: # Replace with a single Otechie username 13 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 14 | -------------------------------------------------------------------------------- /ADMF/internal/data/context/contextPromptChecked.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Tests, whether the context should be checked by default. 4 | 5 | .DESCRIPTION 6 | If this script returns true, then the context will be automatically checked when setting up the prompt GUI. 7 | For performance reasons, this file should be removed if there is no way this can ever be $true 8 | 9 | This allows reducing user error, by smartly detecting whether a context should be checked, when you have a way to determine that automatically. 10 | 11 | .PARAMETER Server 12 | The server / domain to work with. 13 | 14 | .PARAMETER Credential 15 | The credentials to use for this operation. 16 | #> 17 | param ( 18 | [PSFComputer] 19 | $Server, 20 | 21 | [System.Management.Automation.PSCredential] 22 | $Credential 23 | ) 24 | 25 | $false -------------------------------------------------------------------------------- /library/ADMF/ADMF/UpdateForestOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ADMF 4 | { 5 | [Flags] 6 | public enum UpdateForestOptions 7 | { 8 | Sites = 1, 9 | Subnets = 2, 10 | SiteLinks = 4, 11 | ServerRelocate = 8, 12 | Schema = 16, 13 | SchemaLdif = 32, 14 | NTAuthStore = 64, 15 | ForestLevel = 128, 16 | ExchangeSchema = 256, 17 | SchemaDefaultPermissions = 512, 18 | Certificates = 1024, 19 | SchemaManage = 2048, 20 | 21 | Default = Sites | Subnets | SiteLinks | NTAuthStore | Certificates, 22 | All = Default | ServerRelocate | ForestLevel | AllSchema, 23 | Topology = Sites | Subnets | SiteLinks, 24 | AllSchema = Schema | SchemaLdif | ExchangeSchema | SchemaDefaultPermissions 25 | } 26 | } -------------------------------------------------------------------------------- /ADMF/functions/Get-AdmfContextStore.ps1: -------------------------------------------------------------------------------- 1 | function Get-AdmfContextStore 2 | { 3 | <# 4 | .SYNOPSIS 5 | Returns the list of available context stores. 6 | 7 | .DESCRIPTION 8 | Returns the list of available context stores. 9 | 10 | .PARAMETER Name 11 | The name to filter by. 12 | 13 | .EXAMPLE 14 | PS C:\> Get-AdmfContextStore 15 | 16 | Returns all available context stores. 17 | #> 18 | [CmdletBinding()] 19 | param ( 20 | [string] 21 | $Name = '*' 22 | ) 23 | 24 | process 25 | { 26 | foreach ($config in (Get-PSFConfig -FullName "ADMF.Context.Store.$Name")) 27 | { 28 | [PSCustomObject]@{ 29 | PSTypeName = 'ADMF.Context.Store' 30 | Name = $config.Name -replace '^Context\.Store\.' 31 | Path = $config.Value 32 | PathExists = (Test-Path $config.Value) 33 | } 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /ADMF/internal/data/context/forest/readme.md: -------------------------------------------------------------------------------- 1 | # Forest 2 | 3 | ## Forest Level 4 | 5 | You can define the functional level of the forest in configuration. 6 | To do so, place a file named `forest_level.json` straight in the forest folder. 7 | 8 | This file should be a simple json object with a single property: Level. 9 | 10 | Example content: 11 | 12 | ```json 13 | { 14 | "Level": "2016" 15 | } 16 | ``` 17 | 18 | Supported values: 2008R2, 2012, 2012R2 or 2016 19 | 20 | > It is impossible to downgrade a forest functional level, so even if you define a low level, it will not be applied if the forest is already at a higher level. 21 | 22 | > It is impossible to upgrade the forest to a level not yet reached by all member domains. 23 | In order to upgrade the forest functional level, be sure to first update every single domain. 24 | -------------------------------------------------------------------------------- /ADMF/internal/tepp/credentialProvider.Tepp.ps1: -------------------------------------------------------------------------------- 1 | Register-PSFTeppScriptblock -Name 'ADMF.CredentialProvider' -ScriptBlock { 2 | $module = Get-Module ADMF 3 | if (-not $module) { return } 4 | & $module { $script:credentialProviders.Keys } 5 | } 6 | Register-PSFTeppArgumentCompleter -Command Test-AdmfDomain -Parameter CredentialProvider -Name 'ADMF.CredentialProvider' 7 | Register-PSFTeppArgumentCompleter -Command Invoke-AdmfDomain -Parameter CredentialProvider -Name 'ADMF.CredentialProvider' 8 | Register-PSFTeppArgumentCompleter -Command Test-AdmfForest -Parameter CredentialProvider -Name 'ADMF.CredentialProvider' 9 | Register-PSFTeppArgumentCompleter -Command Invoke-AdmfForest -Parameter CredentialProvider -Name 'ADMF.CredentialProvider' 10 | Register-PSFTeppArgumentCompleter -Command Invoke-AdmfItem -Parameter CredentialProvider -Name 'ADMF.CredentialProvider' -------------------------------------------------------------------------------- /ADMF/internal/data/exchangeDefaults/accessRules/msExchangeSGExchangeServers.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "Path": "CN=Exchange Servers,OU=Microsoft Exchange Security Groups,%DomainDN%", 4 | "ActiveDirectoryRights": "GenericAll", 5 | "InheritanceType": "All", 6 | "ObjectType": "", 7 | "InheritedObjectType": "", 8 | "AccessControlType": "Allow", 9 | "Identity": "%DomainNetBIOSName%\\Organization Management" 10 | }, 11 | { 12 | "Path": "CN=Exchange Servers,OU=Microsoft Exchange Security Groups,%DomainDN%", 13 | "ActiveDirectoryRights": "ReadProperty, WriteProperty", 14 | "InheritanceType": "All", 15 | "ObjectType": "Member", 16 | "InheritedObjectType": "", 17 | "AccessControlType": "Allow", 18 | "Identity": "%DomainSID%-512" 19 | } 20 | ] -------------------------------------------------------------------------------- /ADMF/internal/data/exchangeSPDefaults/accessRules/msExchangeOtherObjects.psd1: -------------------------------------------------------------------------------- 1 | @{ 2 | Path = 'CN=Monitoring Mailboxes,CN=Microsoft Exchange System Objects,%DomainDN%' 3 | Identity = '%DomainNetBIOSName%\Exchange Servers' 4 | ActiveDirectoryRights = 'DeleteTree' 5 | InheritanceType = 'All' 6 | AccessControlType = 'Allow' 7 | ObjectType = '' 8 | InheritedObjectType = '' 9 | } 10 | @{ 11 | Path = 'CN=Exchange Install Domain Servers,CN=Microsoft Exchange System Objects,%DomainDN%' 12 | Identity = '%DomainNetBIOSName%\Organization Management' 13 | ActiveDirectoryRights = 'GenericAll' 14 | InheritanceType = 'All' 15 | AccessControlType = 'Allow' 16 | ObjectType = '' 17 | InheritedObjectType = '' 18 | } -------------------------------------------------------------------------------- /ADMF/internal/data/exchangeSPDefaults/accessRules/msExchangeSGExchangeServers.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "Path": "CN=Exchange Servers,OU=Microsoft Exchange Security Groups,%DomainDN%", 4 | "ActiveDirectoryRights": "GenericAll", 5 | "InheritanceType": "All", 6 | "ObjectType": "", 7 | "InheritedObjectType": "", 8 | "AccessControlType": "Allow", 9 | "Identity": "%DomainNetBIOSName%\\Organization Management" 10 | }, 11 | { 12 | "Path": "CN=Exchange Servers,OU=Microsoft Exchange Security Groups,%DomainDN%", 13 | "ActiveDirectoryRights": "ReadProperty, WriteProperty", 14 | "InheritanceType": "All", 15 | "ObjectType": "Member", 16 | "InheritedObjectType": "", 17 | "AccessControlType": "Allow", 18 | "Identity": "%DomainSID%-512" 19 | } 20 | ] -------------------------------------------------------------------------------- /ADMF/internal/components/ExchangeSPDefaults/domain/accessRules/msExchangeOtherObjects.psd1: -------------------------------------------------------------------------------- 1 | @{ 2 | Path = 'CN=Monitoring Mailboxes,CN=Microsoft Exchange System Objects,%DomainDN%' 3 | Identity = '%DomainNetBIOSName%\Exchange Servers' 4 | ActiveDirectoryRights = 'DeleteTree' 5 | InheritanceType = 'All' 6 | AccessControlType = 'Allow' 7 | ObjectType = '' 8 | InheritedObjectType = '' 9 | } 10 | @{ 11 | Path = 'CN=Exchange Install Domain Servers,CN=Microsoft Exchange System Objects,%DomainDN%' 12 | Identity = '%DomainNetBIOSName%\Organization Management' 13 | ActiveDirectoryRights = 'GenericAll' 14 | InheritanceType = 'All' 15 | AccessControlType = 'Allow' 16 | ObjectType = '' 17 | InheritedObjectType = '' 18 | } -------------------------------------------------------------------------------- /ADMF/internal/data/domainDefaults/accessRules/addefault_systemDomainUpdates.psd1: -------------------------------------------------------------------------------- 1 | @{ 2 | ObjectCategory = 'systemDomainUpdates' 3 | ActiveDirectoryRights = "GenericAll" 4 | InheritanceType = "None" 5 | ObjectType = "" 6 | InheritedObjectType = "" 7 | AccessControlType = "Allow" 8 | Identity = "%DomainSID%-512" 9 | Present = 'false' 10 | } 11 | @{ 12 | ObjectCategory = 'systemDomainUpdates' 13 | ActiveDirectoryRights = "CreateChild, DeleteChild, Self, WriteProperty, ExtendedRight, GenericRead, WriteDacl, WriteOwner" 14 | InheritanceType = "None" 15 | ObjectType = "" 16 | InheritedObjectType = "" 17 | AccessControlType = "Allow" 18 | Identity = "%DomainSID%-512" 19 | Present = 'undefined' 20 | } -------------------------------------------------------------------------------- /ADMF/internal/components/ExchangeDefaults/domain/accessRules/msExchangeSGExchangeServers.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "Path": "CN=Exchange Servers,OU=Microsoft Exchange Security Groups,%DomainDN%", 4 | "ActiveDirectoryRights": "GenericAll", 5 | "InheritanceType": "All", 6 | "ObjectType": "", 7 | "InheritedObjectType": "", 8 | "AccessControlType": "Allow", 9 | "Identity": "%DomainNetBIOSName%\\Organization Management" 10 | }, 11 | { 12 | "Path": "CN=Exchange Servers,OU=Microsoft Exchange Security Groups,%DomainDN%", 13 | "ActiveDirectoryRights": "ReadProperty, WriteProperty", 14 | "InheritanceType": "All", 15 | "ObjectType": "Member", 16 | "InheritedObjectType": "", 17 | "AccessControlType": "Allow", 18 | "Identity": "%DomainSID%-512" 19 | } 20 | ] -------------------------------------------------------------------------------- /ADMF/internal/components/ExchangeSPDefaults/domain/accessRules/msExchangeSGExchangeServers.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "Path": "CN=Exchange Servers,OU=Microsoft Exchange Security Groups,%DomainDN%", 4 | "ActiveDirectoryRights": "GenericAll", 5 | "InheritanceType": "All", 6 | "ObjectType": "", 7 | "InheritedObjectType": "", 8 | "AccessControlType": "Allow", 9 | "Identity": "%DomainNetBIOSName%\\Organization Management" 10 | }, 11 | { 12 | "Path": "CN=Exchange Servers,OU=Microsoft Exchange Security Groups,%DomainDN%", 13 | "ActiveDirectoryRights": "ReadProperty, WriteProperty", 14 | "InheritanceType": "All", 15 | "ObjectType": "Member", 16 | "InheritedObjectType": "", 17 | "AccessControlType": "Allow", 18 | "Identity": "%DomainSID%-512" 19 | } 20 | ] -------------------------------------------------------------------------------- /ADMF/internal/components/DefaultAccessRules/domain/accessRules/addefault_systemDomainUpdates.psd1: -------------------------------------------------------------------------------- 1 | @{ 2 | ObjectCategory = 'systemDomainUpdates' 3 | ActiveDirectoryRights = "GenericAll" 4 | InheritanceType = "None" 5 | ObjectType = "" 6 | InheritedObjectType = "" 7 | AccessControlType = "Allow" 8 | Identity = "%DomainSID%-512" 9 | Present = 'false' 10 | } 11 | @{ 12 | ObjectCategory = 'systemDomainUpdates' 13 | ActiveDirectoryRights = "CreateChild, DeleteChild, Self, WriteProperty, ExtendedRight, GenericRead, WriteDacl, WriteOwner" 14 | InheritanceType = "None" 15 | ObjectType = "" 16 | InheritedObjectType = "" 17 | AccessControlType = "Allow" 18 | Identity = "%DomainSID%-512" 19 | Present = 'undefined' 20 | } -------------------------------------------------------------------------------- /ADMF/internal/data/context/forest/ntAuthStore/readme.md: -------------------------------------------------------------------------------- 1 | # NTAuthStore 2 | 3 | ## Synopsis 4 | 5 | The NTAuthStore component allows you to define certificates to apply to the central store of trusted certificates. 6 | Useful for rolling out trust to a central PKI in a dedicated forest. 7 | 8 | ## Authorative and non-Authorative 9 | 10 | By default, the Component only ADDS certificates to the NTAuthStore without removing certificates not defined (non-Authorative). 11 | 12 | Switching to Authorative mode causes it to remove undefined certificates. 13 | 14 | ## Defining Configuration 15 | 16 | To define the certificates to register, simply drop the .cer files in this folder. 17 | All certificates thus found will be included. 18 | 19 | > Authorative Mode 20 | 21 | To define the authorative mode, drop a json file in this folder, formed like this: 22 | 23 | ```json 24 | { 25 | Authorative: true 26 | } 27 | ``` 28 | -------------------------------------------------------------------------------- /ADMF/internal/data/context/domain/wmifilter/readme.md: -------------------------------------------------------------------------------- 1 | # WMI Filter 2 | 3 | ## Synopsis 4 | 5 | Add configuration data to manage wmi filters 6 | 7 | Place any number of json/psd1 files in this folder. 8 | Each file should contain an array of objects. 9 | What properties to place on each object depends on the parameters present on the relevant 'Register-*'-Command. 10 | For a full description of the supported properties, see the parameter descriptions: 11 | 12 | Get-Help Register-DMWmiFilter -Detailed 13 | 14 | ## Example 15 | 16 | Example configuration using the psd1 file format: 17 | 18 | ```powershell 19 | @{ 20 | Name = 'Client' 21 | Description = 'Applies to all client OS versions' 22 | Query = 'SELECT * FROM Win32_OperatingSystem WHERE ProductType = 1' 23 | # Namespace = 'root\CIMv2' 24 | Author = 'Friedrich Weinmann' 25 | CreatedOn = '2022-10-21' 26 | } 27 | ``` 28 | -------------------------------------------------------------------------------- /ADMF/tests/general/Help.Exceptions.ps1: -------------------------------------------------------------------------------- 1 | # List of functions that should be ignored 2 | $global:FunctionHelpTestExceptions = @( 3 | 4 | ) 5 | 6 | <# 7 | List of arrayed enumerations. These need to be treated differently. Add full name. 8 | Example: 9 | 10 | "Sqlcollaborative.Dbatools.Connection.ManagementConnectionType[]" 11 | #> 12 | $global:HelpTestEnumeratedArrays = @( 13 | "UpdateDCOptions[]" 14 | "UpdateDomainOptions[]" 15 | "UpdateForestOptions[]" 16 | "ADMF.UpdateDomainOptions[]" 17 | "ADMF.UpdateDCOptions[]" 18 | "ADMF.UpdateForestOptions[]" 19 | ) 20 | 21 | <# 22 | Some types on parameters just fail their validation no matter what. 23 | For those it becomes possible to skip them, by adding them to this hashtable. 24 | Add by following this convention: = @() 25 | Example: 26 | 27 | "Get-DbaCmObject" = @("DoNotUse") 28 | #> 29 | $global:HelpTestSkipParameterType = @{ 30 | 31 | } -------------------------------------------------------------------------------- /ADMF/tests/general/FileIntegrity.Exceptions.ps1: -------------------------------------------------------------------------------- 1 | # List of forbidden commands 2 | $global:BannedCommands = @( 3 | 'Write-Host', 4 | 'Write-Verbose', 5 | 'Write-Warning', 6 | 'Write-Error', 7 | 'Write-Output', 8 | 'Write-Information', 9 | 'Write-Debug', 10 | 11 | # Use CIM instead where possible 12 | 'Get-WmiObject', 13 | 'Invoke-WmiMethod', 14 | 'Register-WmiEvent', 15 | 'Remove-WmiObject', 16 | 'Set-WmiInstance' 17 | ) 18 | 19 | <# 20 | Contains list of exceptions for banned cmdlets. 21 | Insert the file names of files that may contain them. 22 | 23 | Example: 24 | "Write-Host" = @('Write-PSFHostColor.ps1','Write-PSFMessage.ps1') 25 | #> 26 | $global:MayContainCommand = @{ 27 | "Write-Host" = @() 28 | "Write-Verbose" = @() 29 | "Write-Warning" = @() 30 | "Write-Error" = @('Test-AdmfDomain.ps1', 'Invoke-AdmfDomain.ps1', 'Test-AdmfForest.ps1', 'Invoke-AdmfForest.ps1') 31 | "Write-Output" = @('Set-AdmfContext.ps1') 32 | "Write-Information" = @() 33 | "Write-Debug" = @() 34 | } -------------------------------------------------------------------------------- /ADMF/internal/data/context/dc/readme.md: -------------------------------------------------------------------------------- 1 | # DC Configuration 2 | 3 | ## Deployment Options 4 | 5 | There are a few considerations specific to deploying domain controllers: 6 | 7 | + Should it use AD integrated DNS? 8 | + Do we want to reboot after promoting a DC? 9 | + What paths to use for database, logs or sysvol? 10 | 11 | These can be configured in this folder, however those can also be defined per parameter when calling any of the Install-DC* commands. 12 | To configure any of these settings, create a file in this folder named `dc_config.json` and define the values. 13 | 14 | Example configuration file content: 15 | 16 | ```json 17 | { 18 | "NoDNS": false, 19 | "NoReboot": false, 20 | "LogPath": "C:\\Windows\\NTDS", 21 | "SysvolPath": "C:\\Windows\\SYSVOL", 22 | "DatabasePath": "C:\\Windows\\NTDS" 23 | } 24 | ``` 25 | 26 | These in fact are the default values used when no configuration is provided. 27 | Only define properties you want to define - a partial configuration is totally fine. 28 | -------------------------------------------------------------------------------- /ADMF/tests/readme.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | This is the folder, where all the tests go. 4 | 5 | Those are subdivided in two categories: 6 | 7 | - General 8 | - Function 9 | 10 | ## General Tests 11 | 12 | General tests are function generic and test for general policies. 13 | 14 | These test scan answer questions such as: 15 | 16 | - Is my module following my style guides? 17 | - Does any of my scripts have a syntax error? 18 | - Do my scripts use commands I do not want them to use? 19 | - Do my commands follow best practices? 20 | - Do my commands have proper help? 21 | 22 | Basically, these allow a general module health check. 23 | 24 | These tests are already provided as part of the template. 25 | 26 | ## Function Tests 27 | 28 | A healthy module should provide unit and integration tests for the commands & components it ships. 29 | Only then can be guaranteed, that they will actually perform as promised. 30 | 31 | However, as each such test must be specific to the function it tests, there cannot be much in the way of templates. -------------------------------------------------------------------------------- /ADMF/internal/tepp/readme.md: -------------------------------------------------------------------------------- 1 | # Tab Expansion 2 | 3 | ## Description 4 | 5 | Modern Tab Expansion was opened to users with the module `Tab Expansion Plus Plus` (TEPP). 6 | 7 | It allows you to define, what options a user is offered when tabbing through input options. This can save a lot of time for the user and is considered a key element in user experience. 8 | 9 | The `PSFramework` offers a simplified way of offering just this, as the two example files show. 10 | 11 | ## Concept 12 | 13 | Custom tab completion is defined in two steps: 14 | 15 | - Define a scriptblock that is run when the user hits `TAB` and provides the strings that are his options. 16 | - Assign that scriptblock to the parameter of a command. You can assign the same scriptblock multiple times. 17 | 18 | ## Structure 19 | 20 | Import order matters. In order to make things work with the default scaffold, follow those rules: 21 | 22 | - All scriptfiles _defining_ completion scriptblocks like this: `*.tepp.ps1` 23 | - Put all your completion assignments in `assignment.ps1` -------------------------------------------------------------------------------- /azFunctionResources/functionOverride/Get-Example.ps1: -------------------------------------------------------------------------------- 1 | function Get-Example 2 | { 3 | <# 4 | .NOTES 5 | This file will be used to override the auto-generated CLIENT MODULE function implementation. 6 | 7 | Using the vsts-createFunctionClientModule.ps1 task script, you can autogenerate a client module. 8 | That module can be used to connect to the published Azure Function Module. 9 | However sometimes you may want to override the default client function for a given command, 10 | in order to better customize the way it behaves. 11 | 12 | Creating a ps1 file with the name of the specific function in this folder will use this file, 13 | rather than creating a default copy. 14 | 15 | NOTE: 16 | There will be no further automatic change detection! 17 | If you later update the Azure Function, you need to manually update the client function as well. 18 | #> 19 | [CmdletBinding()] 20 | Param ( 21 | 22 | ) 23 | 24 | begin 25 | { 26 | 27 | } 28 | process 29 | { 30 | 31 | } 32 | end 33 | { 34 | 35 | } 36 | } -------------------------------------------------------------------------------- /ADMF/internal/data/context/domain/names/readme.md: -------------------------------------------------------------------------------- 1 | # Names 2 | 3 | ## Synopsis 4 | 5 | Add configuration data to register names for name mapping. 6 | This allows you to add custom variables into your other domain definitions which will then be expanded into the full string. 7 | For example, when defining a groupname, you could define it in configuration with a name such as "SEC-%ENV%-Network-Admins". 8 | Then register the appropriate string under the name "ENV". 9 | See parameter help on each individual register command to see which property supports name expansion. 10 | Generally, all name, path or description properties should, as well as all properties referring to such a property on another object. 11 | 12 | Place any number of json files in this folder. 13 | Each file should contain an array of objects. 14 | What properties to place on each object depends on the parameters present on the relevant 'Register-*'-Command. 15 | For a full description of the supported properties, see the parameter descriptions: 16 | 17 | Get-Help Register-DMNameMapping -Detailed 18 | 19 | -------------------------------------------------------------------------------- /ADMF/readme.md: -------------------------------------------------------------------------------- 1 | # PSFModule guidance 2 | 3 | This is a finished module layout optimized for implementing the PSFramework. 4 | 5 | If you don't care to deal with the details, this is what you need to do to get started seeing results: 6 | 7 | - Add the functions you want to publish to `/functions/` 8 | - Update the `FunctionsToExport` node in the module manifest (ADMF.psd1). All functions you want to publish should be in a list. 9 | - Add internal helper functions the user should not see to `/internal/functions/` 10 | 11 | ## Path Warning 12 | 13 | > If you want your module to be compatible with Linux and MacOS, keep in mind that those OS are case sensitive for paths and files. 14 | 15 | `Import-ModuleFile` is preconfigured to resolve the path of the files specified, so it will reliably convert weird path notations the system can't handle. 16 | Content imported through that command thus need not mind the path separator. 17 | If you want to make sure your code too will survive OS-specific path notations, get used to using `Resolve-path` or the more powerful `Resolve-PSFPath`. -------------------------------------------------------------------------------- /ADMF/internal/scriptblocks/scriptblocks.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | Stored scriptblocks are available in [PsfValidateScript()] attributes. 3 | This makes it easier to centrally provide the same scriptblock multiple times, 4 | without having to maintain it in separate locations. 5 | 6 | It also prevents lengthy validation scriptblocks from making your parameter block 7 | hard to read. 8 | 9 | Set-PSFScriptblock -Name 'ADMF.ScriptBlockName' -Scriptblock { 10 | 11 | } 12 | #> 13 | Set-PSFScriptblock -Name 'ADMF.Validate.Type.Gpo' -Scriptblock { 14 | foreach ($item in $_) { 15 | if ($item.PSObject.TypeNames -notcontains 'Microsoft.GroupPolicy.Gpo') { return $false } 16 | } 17 | $true 18 | } 19 | Set-PSFScriptblock -Name 'ADMF.Validate.Path' -Scriptblock { 20 | Test-Path -Path $_ 21 | } 22 | Set-PSFScriptblock -Name 'ADMF.Validate.Path.Folder' -Scriptblock { 23 | $resolvedPath = Resolve-PSFPath -Provider FileSystem -Path $_ -SingleItem 24 | Test-Path -Path $resolvedPath -PathType Container 25 | } 26 | Set-PSFScriptblock -Name 'ADMF.Validate.ContextStore.ExistsNot' -Scriptblock { 27 | $_ -notin (Get-AdmfContextStore).Name 28 | } -------------------------------------------------------------------------------- /ADMF/xml/ADMF.Types.ps1xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Deserialized.Foo.Bar 6 | 7 | 8 | PSStandardMembers 9 | 10 | 11 | 12 | TargetTypeForDeserialization 13 | 14 | 15 | Foo.Bar 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | Foo.Bar 24 | 25 | 26 | SerializationData 27 | 28 | PSFramework.Serialization.SerializationTypeConverter 29 | GetSerializationData 30 | 31 | 32 | 33 | 34 | PSFramework.Serialization.SerializationTypeConverter 35 | 36 | 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Friedrich Weinmann 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /ADMF/tests/general/strings.Tests.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .DESCRIPTION 3 | This test verifies, that all strings that have been used, 4 | are listed in the language files and thus have a message being displayed. 5 | 6 | It also checks, whether the language files have orphaned entries that need cleaning up. 7 | #> 8 | 9 | 10 | 11 | Describe "Testing localization strings" { 12 | $moduleRoot = (Get-Module ADMF).ModuleBase 13 | $stringsResults = Export-PSMDString -ModuleRoot $moduleRoot 14 | $exceptions = & "$global:testroot\general\strings.Exceptions.ps1" 15 | 16 | foreach ($stringEntry in $stringsResults) { 17 | if ($stringEntry.String -eq "key") { continue } # Skipping the template default entry 18 | It "Should be used & have text: $($stringEntry.String)" -TestCases @{ stringEntry = $stringEntry; exceptions = $exceptions } { 19 | if ($exceptions.LegalSurplus -notcontains $stringEntry.String) { 20 | $stringEntry.Surplus | Should -BeFalse 21 | } 22 | if ($exceptions.NoTextNeeded -notcontains $stringEntry.String) { 23 | $stringEntry.Text | Should -Not -BeNullOrEmpty 24 | } 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /ADMF/internal/functions/Resolve-DataFile.ps1: -------------------------------------------------------------------------------- 1 | function Resolve-DataFile { 2 | <# 3 | .SYNOPSIS 4 | Resolves the specified file to a datafile path, no matter whether it is json or psd1 5 | 6 | .DESCRIPTION 7 | Resolves the specified file to a datafile path, no matter whether it is json or psd1 8 | Will prioritize json over psd1 if both are present. 9 | Will return an empty value if neither exists. 10 | 11 | .PARAMETER Path 12 | Path to the file to resolve. 13 | Do not specify an extension, if you want to aim for both of them. 14 | 15 | .EXAMPLE 16 | PS C:\> Resolve-DataFile -Path ".\config" 17 | 18 | Will resolve to either ".\config.json" or ".\config.psd1", depending on which is available. 19 | #> 20 | [OutputType([string])] 21 | [CmdletBinding()] 22 | param ( 23 | [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] 24 | [string] 25 | $Path 26 | ) 27 | 28 | process { 29 | if (Test-Path -Path $Path) { 30 | return $Path 31 | } 32 | 33 | if (Test-Path -Path "$Path.json") { 34 | return "$Path.json" 35 | } 36 | 37 | if (Test-Path -Path "$Path.psd1") { 38 | return "$Path.psd1" 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /library/ADMF/ADMF.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.27130.2010 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{DE531953-A405-4C22-B008-4DC0B0CA4C3D}") = "ADMF", "ADMF\ADMF.csproj", "{269912E3-9131-491F-B777-E4418E51660D}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {269912E3-9131-491F-B777-E4418E51660D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {269912E3-9131-491F-B777-E4418E51660D}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {269912E3-9131-491F-B777-E4418E51660D}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {269912E3-9131-491F-B777-E4418E51660D}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {A7BB47E3-B591-44A9-9F5D-EE2D9C634AF4} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /library/ADMF/ADMF/UpdateDomainOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ADMF 4 | { 5 | [Flags] 6 | public enum UpdateDomainOptions 7 | { 8 | AccessRule = 1, 9 | Acl = 2, 10 | GPLink = 4, 11 | GPPermission = 8, 12 | GroupPolicy = 16, 13 | GroupMembership = 32, 14 | Group = 64, 15 | OUSoft = 128, 16 | OUHard = 256, 17 | PSO = 512, 18 | User = 1024, 19 | GPLinkDisable = 2048, 20 | GroupPolicyDelete = 4096, 21 | Object = 8192, 22 | DomainLevel = 16384, 23 | ServiceAccount = 32768, 24 | Exchange = 65536, 25 | GPOwner = 131072, 26 | WmiFilter = 262144, 27 | 28 | AllGP = GPLink | GPPermission | GPOwner | GroupPolicy | GPLinkDisable | GroupPolicyDelete | WmiFilter, 29 | Security = AccessRule | Acl | PSO, 30 | AllLinks = AccessRule | Acl | GPLink | GPPermission | GroupMembership | PSO | GPLinkDisable, 31 | AllContent = GroupMembership | Group | OUSoft | OUHard | User | Group | ServiceAccount | Object, 32 | Default = Acl | PSO | AllGP | AllContent, 33 | All = AccessRule | DomainLevel | Default 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /ADMF/internal/data/context/domain/objectcategories/readme.md: -------------------------------------------------------------------------------- 1 | # Object Categories 2 | 3 | ## Synopsis 4 | 5 | Add configuration data to manage object categories. 6 | Some rules & settings can be applied to objects of a given Object Category, rather than a specific identity. 7 | 8 | Each file must be a psd1 file returning a single hashtable containing the following key/value pairs: 9 | 10 | - Name: The name by which to address the category. 11 | - ObjectClass: The object class this category applies to. Important for performance reasons. 12 | - Property: The properties needed to verify, whether a given object of the defined objectclass is also part of this category. 13 | - TestScript: A scriptblock that returns $true if the first argument (an AD object) is part of the category. 14 | - LDAPFilter: An LDAP filter to retrieve all objects of this category. 15 | 16 | Example category from the default access rule set: 17 | 18 | ```powershell 19 | @{ 20 | Name = 'DomainController' 21 | ObjectClass = 'computer' 22 | Property = @('PrimaryGroupID') 23 | TestScript = { $args[0].PrimaryGroupID -eq 516 } 24 | LDAPFilter = '(&(objectCategory=computer)(primaryGroupID=516))' 25 | } 26 | ``` 27 | 28 | For more details, see: 29 | 30 | Get-Help Register-DMObjectCategory -Detailed 31 | -------------------------------------------------------------------------------- /ADMF/tests/general/strings.Exceptions.ps1: -------------------------------------------------------------------------------- 1 | $exceptions = @{ } 2 | 3 | <# 4 | A list of entries that MAY be in the language files, without causing the tests to fail. 5 | This is commonly used in modules that generate localized messages straight from C#. 6 | Specify the full key as it is written in the language files, do not prepend the modulename, 7 | as you would have to in C# code. 8 | 9 | Example: 10 | $exceptions['LegalSurplus'] = @( 11 | 'Exception.Streams.FailedCreate' 12 | 'Exception.Streams.FailedDispose' 13 | ) 14 | #> 15 | $exceptions['LegalSurplus'] = @( 16 | 'ADMF.Invoke-AdmfItem.Processing.ShouldProcess' 17 | 'Invoke-AdmfItem.Processing.ShouldProcess' 18 | ) 19 | <# 20 | A list of entries that MAY be used without needing to have text defined. 21 | This is intended for modules (re-)using strings provided by another module 22 | #> 23 | $exceptions['NoTextNeeded'] = @( 24 | 'Validate.FSPath' 25 | 'Validate.FSPath.File' 26 | 'Validate.FSPath.FileOrParent' 27 | 'Validate.FSPath.Folder' 28 | 'Validate.Path' 29 | 'Validate.Path.Container' 30 | 'Validate.Path.Leaf' 31 | 'Validate.TimeSpan.Positive' 32 | 'Validate.Uri.Absolute' 33 | 'Validate.Uri.Absolute.File' 34 | 'Validate.Uri.Absolute.Https' 35 | 36 | 'ADMF.Invoke-AdmfItem.Processing.ShouldProcess' 37 | 'Invoke-AdmfItem.Processing.ShouldProcess' 38 | ) 39 | 40 | $exceptions -------------------------------------------------------------------------------- /ADMF/internal/scripts/license.ps1: -------------------------------------------------------------------------------- 1 | New-PSFLicense -Product 'ADMF' -Manufacturer 'Friedrich Weinmann' -ProductVersion $script:ModuleVersion -ProductType Module -Name MIT -Version "1.0.0.0" -Date (Get-Date "2019-12-20") -Text @" 2 | Copyright (c) 2019 Friedrich Weinmann 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | "@ -------------------------------------------------------------------------------- /ADMF/tests/general/PSScriptAnalyzer.Tests.ps1: -------------------------------------------------------------------------------- 1 | [CmdletBinding()] 2 | Param ( 3 | [switch] 4 | $SkipTest, 5 | 6 | [string[]] 7 | $CommandPath = @("$global:testroot\..\functions", "$global:testroot\..\internal\functions") 8 | ) 9 | 10 | if ($SkipTest) { return } 11 | 12 | $global:__pester_data.ScriptAnalyzer = New-Object System.Collections.ArrayList 13 | 14 | Describe 'Invoking PSScriptAnalyzer against commandbase' { 15 | 16 | $commandFiles = foreach ($pathItem in $CommandPath) { 17 | Get-ChildItem -Path $pathItem -Recurse | Where-Object Name -like "*.ps1" 18 | } 19 | $scriptAnalyzerRules = Get-ScriptAnalyzerRule 20 | 21 | foreach ($file in $commandFiles) 22 | { 23 | Context "Analyzing $($file.BaseName)" { 24 | $analysis = Invoke-ScriptAnalyzer -Path $file.FullName -ExcludeRule PSAvoidTrailingWhitespace, PSShouldProcess 25 | 26 | forEach ($rule in $scriptAnalyzerRules) 27 | { 28 | It "Should pass $rule" -TestCases @{ analysis = $analysis; rule = $rule } { 29 | If ($analysis.RuleName -contains $rule) 30 | { 31 | $analysis | Where-Object RuleName -EQ $rule -outvariable failures | ForEach-Object { $null = $global:__pester_data.ScriptAnalyzer.Add($_) } 32 | 33 | 1 | Should -Be 0 34 | } 35 | else 36 | { 37 | 0 | Should -Be 0 38 | } 39 | } 40 | } 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /ADMF/internal/scripts/postimport.ps1: -------------------------------------------------------------------------------- 1 | # Add all things you want to run after importing the main code 2 | 3 | # Load Configuration Validations 4 | foreach ($file in (Get-ChildItem "$($script:ModuleRoot)\internal\configurationValidation\*.ps1" -ErrorAction Ignore)) { 5 | . Import-ModuleFile -Path $file.FullName 6 | } 7 | 8 | # Load Configurations 9 | foreach ($file in (Get-ChildItem "$($script:ModuleRoot)\internal\configurations\*.ps1" -ErrorAction Ignore)) { 10 | . Import-ModuleFile -Path $file.FullName 11 | } 12 | 13 | # Load Scriptblocks 14 | foreach ($file in (Get-ChildItem "$($script:ModuleRoot)\internal\scriptblocks\*.ps1" -ErrorAction Ignore)) { 15 | . Import-ModuleFile -Path $file.FullName 16 | } 17 | 18 | # Load Tab Expansion 19 | foreach ($file in (Get-ChildItem "$($script:ModuleRoot)\internal\tepp\*.tepp.ps1" -ErrorAction Ignore)) { 20 | . Import-ModuleFile -Path $file.FullName 21 | } 22 | 23 | # Load Tab Expansion Assignment 24 | . Import-ModuleFile -Path "$($script:ModuleRoot)\internal\tepp\assignment.ps1" 25 | 26 | # Load License 27 | . Import-ModuleFile -Path "$($script:ModuleRoot)\internal\scripts\license.ps1" 28 | 29 | # Load Variables 30 | . Import-ModuleFile -Path "$($script:ModuleRoot)\internal\scripts\variables.ps1" 31 | 32 | # Initialize some content 33 | . Import-ModuleFile -Path "$($script:ModuleRoot)\internal\scripts\initialize.ps1" -------------------------------------------------------------------------------- /ADMF/internal/data/domainDefaults/gppermissions/addefault_allPermissions.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "Filter": "-not (DefaultDomainPolicy)", 4 | "Identity": "%DomainSID%-512", 5 | "ObjectClass": "Group", 6 | "Permission": "GpoEditDeleteModifySecurity" 7 | }, 8 | { 9 | "Filter": "-not (DefaultDomainPolicy)", 10 | "Identity": "%DomainSID%-519", 11 | "ObjectClass": "Group", 12 | "Permission": "GpoEditDeleteModifySecurity" 13 | }, 14 | { 15 | "Filter": "DefaultDomainPolicy", 16 | "Identity": "%DomainSID%-512", 17 | "ObjectClass": "Group", 18 | "Permission": "GpoCustom" 19 | }, 20 | { 21 | "Filter": "DefaultDomainPolicy", 22 | "Identity": "%DomainSID%-519", 23 | "ObjectClass": "Group", 24 | "Permission": "GpoCustom" 25 | }, 26 | { 27 | "All": true, 28 | "Identity": "S-1-5-9", 29 | "ObjectClass": "Group", 30 | "Permission": "GpoRead" 31 | }, 32 | { 33 | "All": true, 34 | "Identity": "S-1-5-11", 35 | "ObjectClass": "Group", 36 | "Permission": "GpoApply" 37 | }, 38 | { 39 | "All": true, 40 | "Identity": "S-1-5-18", 41 | "ObjectClass": "Group", 42 | "Permission": "GpoEditDeleteModifySecurity" 43 | } 44 | ] -------------------------------------------------------------------------------- /ADMF/internal/components/DefaultAccessRules/domain/gppermissions/addefault_allPermissions.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "Filter": "-not (DefaultDomainPolicy)", 4 | "Identity": "%DomainSID%-512", 5 | "ObjectClass": "Group", 6 | "Permission": "GpoEditDeleteModifySecurity" 7 | }, 8 | { 9 | "Filter": "-not (DefaultDomainPolicy)", 10 | "Identity": "%DomainSID%-519", 11 | "ObjectClass": "Group", 12 | "Permission": "GpoEditDeleteModifySecurity" 13 | }, 14 | { 15 | "Filter": "DefaultDomainPolicy", 16 | "Identity": "%DomainSID%-512", 17 | "ObjectClass": "Group", 18 | "Permission": "GpoCustom" 19 | }, 20 | { 21 | "Filter": "DefaultDomainPolicy", 22 | "Identity": "%DomainSID%-519", 23 | "ObjectClass": "Group", 24 | "Permission": "GpoCustom" 25 | }, 26 | { 27 | "All": true, 28 | "Identity": "S-1-5-9", 29 | "ObjectClass": "Group", 30 | "Permission": "GpoRead" 31 | }, 32 | { 33 | "All": true, 34 | "Identity": "S-1-5-11", 35 | "ObjectClass": "Group", 36 | "Permission": "GpoApply" 37 | }, 38 | { 39 | "All": true, 40 | "Identity": "S-1-5-18", 41 | "ObjectClass": "Group", 42 | "Permission": "GpoEditDeleteModifySecurity" 43 | } 44 | ] -------------------------------------------------------------------------------- /ADMF/internal/data/context/domain/exchange/readme.md: -------------------------------------------------------------------------------- 1 | # Exchange System Objects 2 | 3 | ## Description 4 | 5 | This Component ensures that the domain is configured for Exchange. 6 | Without deploying this, users in the specific domain cannot have mailboxes. 7 | 8 | > Deploying Exchange System Objects to a domain requires the forest to already have been prepared for this version. 9 | 10 | ## Example Configuration 11 | 12 | Configure schema updates only to Level of Exchange 2019 CU6: 13 | 14 | ```json 15 | { 16 | "LocalImagePath": "C:\\images\\exchange_2019CU6.iso", 17 | "ExchangeVersion": "2019CU6" 18 | } 19 | ``` 20 | 21 | ## Parameters 22 | 23 | ### LocalImagePath 24 | 25 | The path where to find the Exchange ISO file 26 | Must be local on the remote server connected to! 27 | Updating the Exchange AD settings is only supported when executed through the installer contained in that ISO file without exceptions. 28 | 29 | ### ExchangeVersion 30 | 31 | The version of the Exchange server to apply. 32 | E.g. 2016CU6 33 | We map Exchange versions to their respective identifier in AD: 34 | ObjectVersion in the domain's Microsoft Exchange System Objects container. 35 | This parameter is to help avoiding to have to look up that value. 36 | If your version is not supported by us yet, look up the version number and explicitly bind it to -ObjectVersion instead. 37 | 38 | ### ObjectVersion 39 | 40 | The object version on the "Microsoft Exchange System Objects" container in the domain. 41 | -------------------------------------------------------------------------------- /ADMF/internal/data/domainDefaults/accessRules/addefault_domainObject.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "Path": "%DomainDN%", 4 | "ActiveDirectoryRights": "ExtendedRight", 5 | "InheritanceType": "None", 6 | "ObjectType": "DS-Clone-Domain-Controller", 7 | "InheritedObjectType": "\u003cAll\u003e", 8 | "AccessControlType": "Allow", 9 | "Identity": "%DomainName%\\Cloneable Domain Controllers" 10 | }, 11 | { 12 | "Path": "%DomainDN%", 13 | "ActiveDirectoryRights": "ReadProperty, WriteProperty", 14 | "InheritanceType": "All", 15 | "ObjectType": "ms-DS-Key-Credential-Link", 16 | "InheritedObjectType": "\u003cAll\u003e", 17 | "AccessControlType": "Allow", 18 | "Identity": "%DomainName%\\Key Admins" 19 | }, 20 | { 21 | "Path": "%DomainDN%", 22 | "ActiveDirectoryRights": "ReadProperty, WriteProperty", 23 | "InheritanceType": "All", 24 | "ObjectType": "ms-DS-Key-Credential-Link", 25 | "InheritedObjectType": "\u003cAll\u003e", 26 | "AccessControlType": "Allow", 27 | "Identity": "%RootDomainName%\\Enterprise Key Admins" 28 | }, 29 | { 30 | "Path": "%DomainDN%", 31 | "ActiveDirectoryRights": "DeleteChild", 32 | "InheritanceType": "none", 33 | "ObjectType": "", 34 | "InheritedObjectType": "", 35 | "AccessControlType": "Deny", 36 | "Identity": "S-1-1-0", 37 | "Present": "Undefined" 38 | } 39 | ] -------------------------------------------------------------------------------- /azFunctionResources/profileFunctions/Convert-AzureFunctionParameter.ps1: -------------------------------------------------------------------------------- 1 | function Convert-AzureFunctionParameter 2 | { 3 | <# 4 | .SYNOPSIS 5 | Extracts the parameters passed into the rest method. 6 | 7 | .DESCRIPTION 8 | Extracts the parameters passed into the rest method of an Azure Function. 9 | Returns a hashtable, similar to what would be found on a $PSBoundParameters variable. 10 | 11 | .PARAMETER Request 12 | The request to process 13 | 14 | .EXAMPLE 15 | PS C:\> Convert-AzureFunctionParameter -Request $request 16 | 17 | Converts the $request object into a regular hashtable. 18 | #> 19 | [OutputType([System.Collections.Hashtable])] 20 | [CmdletBinding()] 21 | param ( 22 | $Request 23 | ) 24 | 25 | $parameterObject = [pscustomobject]@{ 26 | Parameters = @{ } 27 | Serialize = $false 28 | } 29 | 30 | foreach ($key in $Request.Query.Keys) 31 | { 32 | # Do NOT include the authentication key 33 | if ($key -eq 'code') { continue } 34 | $parameterObject.Parameters[$key] = $Request.Query.$key 35 | } 36 | foreach ($key in $Request.Body.Keys) 37 | { 38 | $parameterObject.Parameters[$key] = $Request.Body.$key 39 | } 40 | if ($parameterObject.Parameters.__PSSerialize) 41 | { 42 | $parameterObject.Serialize = $true 43 | $null = $parameterObject.Parameters.Remove('__PSSerialize') 44 | } 45 | if ($parameterObject.Parameters.__SerializedParameters) 46 | { 47 | $parameterObject.Parameters = $parameterObject.Parameters.__SerializedParameters | ConvertFrom-PSFClixml 48 | } 49 | 50 | $parameterObject 51 | } -------------------------------------------------------------------------------- /ADMF/internal/components/DefaultAccessRules/domain/accessRules/addefault_domainObject.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "Path": "%DomainDN%", 4 | "ActiveDirectoryRights": "ExtendedRight", 5 | "InheritanceType": "None", 6 | "ObjectType": "DS-Clone-Domain-Controller", 7 | "InheritedObjectType": "\u003cAll\u003e", 8 | "AccessControlType": "Allow", 9 | "Identity": "%DomainName%\\Cloneable Domain Controllers" 10 | }, 11 | { 12 | "Path": "%DomainDN%", 13 | "ActiveDirectoryRights": "ReadProperty, WriteProperty", 14 | "InheritanceType": "All", 15 | "ObjectType": "ms-DS-Key-Credential-Link", 16 | "InheritedObjectType": "\u003cAll\u003e", 17 | "AccessControlType": "Allow", 18 | "Identity": "%DomainName%\\Key Admins" 19 | }, 20 | { 21 | "Path": "%DomainDN%", 22 | "ActiveDirectoryRights": "ReadProperty, WriteProperty", 23 | "InheritanceType": "All", 24 | "ObjectType": "ms-DS-Key-Credential-Link", 25 | "InheritedObjectType": "\u003cAll\u003e", 26 | "AccessControlType": "Allow", 27 | "Identity": "%RootDomainName%\\Enterprise Key Admins" 28 | }, 29 | { 30 | "Path": "%DomainDN%", 31 | "ActiveDirectoryRights": "DeleteChild", 32 | "InheritanceType": "none", 33 | "ObjectType": "", 34 | "InheritedObjectType": "", 35 | "AccessControlType": "Deny", 36 | "Identity": "S-1-1-0", 37 | "Present": "Undefined" 38 | } 39 | ] -------------------------------------------------------------------------------- /azFunctionResources/profileFunctions/Write-AzureFunctionOutput.ps1: -------------------------------------------------------------------------------- 1 | function Write-AzureFunctionOutput 2 | { 3 | <# 4 | .SYNOPSIS 5 | Write output equally well from Azure Functions or locally. 6 | 7 | .DESCRIPTION 8 | Write output equally well from Azure Functions or locally. 9 | When calling this command, call return straight after it. 10 | Use Write-AzureFunctionStatus first if an error should be returned, then specify an error text here. 11 | 12 | .PARAMETER Value 13 | The value data to return. 14 | Either an error message 15 | 16 | .PARAMETER Serialize 17 | Return the output object as compressed clixml string. 18 | You can use ConvertFrom-PSFClixml to restore the object on the recipient-side. 19 | 20 | .EXAMPLE 21 | PS C:\> Write-AzureFunctionOutput -Value $result 22 | 23 | Writes the content of $result as output. 24 | 25 | .EXAMPLE 26 | PS C:\> Write-AzureFunctionOutput -Value $result -Serialize 27 | 28 | Writes the content of $result as output. 29 | If called from Azure Functions, it will convert the output as compressed clixml string. 30 | 31 | #> 32 | [CmdletBinding()] 33 | param ( 34 | [Parameter(Mandatory = $true)] 35 | $Value, 36 | 37 | [switch] 38 | $Serialize, 39 | 40 | [System.Net.HttpStatusCode] 41 | $Status = [System.Net.HttpStatusCode]::OK 42 | ) 43 | 44 | if ($Serialize) 45 | { 46 | $Value = $Value | ConvertTo-PSFClixml 47 | } 48 | 49 | Push-OutputBinding -Name Response -Value ( 50 | [HttpResponseContext]@{ 51 | StatusCode = $Status 52 | Body = $Value 53 | } 54 | ) 55 | } -------------------------------------------------------------------------------- /ADMF/xml/readme.md: -------------------------------------------------------------------------------- 1 | # XML 2 | 3 | This is the folder where project XML files go, notably: 4 | 5 | - Format XML 6 | - Type Extension XML 7 | 8 | External help files should _not_ be placed in this folder! 9 | 10 | ## Notes on Files and Naming 11 | 12 | There should be only one format file and one type extension file per project, as importing them has a notable impact on import times. 13 | 14 | - The Format XML should be named `ADMF.Format.ps1xml` 15 | - The Type Extension XML should be named `ADMF.Types.ps1xml` 16 | 17 | ## Tools 18 | 19 | ### New-PSMDFormatTableDefinition 20 | 21 | This function will take an input object and generate format xml for an auto-sized table. 22 | 23 | It provides a simple way to get started with formats. 24 | 25 | ### Get-PSFTypeSerializationData 26 | 27 | ``` 28 | C# Warning! 29 | This section is only interest if you're using C# together with PowerShell. 30 | ``` 31 | 32 | This function generates type extension XML that allows PowerShell to convert types written in C# to be written to file and restored from it without being 'Deserialized'. Also works for jobs or remoting, if both sides have the `PSFramework` module and type extension loaded. 33 | 34 | In order for a class to be eligible for this, it needs to conform to the following rules: 35 | 36 | - Have the `[Serializable]` attribute 37 | - Be public 38 | - Have an empty constructor 39 | - Allow all public properties/fields to be set (even if setting it doesn't do anything) without throwing an exception. 40 | 41 | ``` 42 | non-public properties and fields will be lost in this process! 43 | ``` -------------------------------------------------------------------------------- /ADMF/internal/data/exchangeDefaults/accessRules/msExchangeSecurityGroups.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "Path": "OU=Microsoft Exchange Security Groups,%DomainDN%", 4 | "ActiveDirectoryRights": "GenericAll", 5 | "InheritanceType": "All", 6 | "ObjectType": "", 7 | "InheritedObjectType": "", 8 | "AccessControlType": "Allow", 9 | "Identity": "%DomainNetBIOSName%\\Organization Management" 10 | }, 11 | { 12 | "Path": "OU=Microsoft Exchange Security Groups,%DomainDN%", 13 | "ActiveDirectoryRights": "Delete", 14 | "InheritanceType": "Descendents", 15 | "ObjectType": "", 16 | "InheritedObjectType": "Group", 17 | "AccessControlType": "Allow", 18 | "Identity": "%DomainNetBIOSName%\\Exchange Trusted Subsystem" 19 | }, 20 | { 21 | "Path": "OU=Microsoft Exchange Security Groups,%DomainDN%", 22 | "ActiveDirectoryRights": "CreateChild", 23 | "InheritanceType": "All", 24 | "ObjectType": "Group", 25 | "InheritedObjectType": "", 26 | "AccessControlType": "Allow", 27 | "Identity": "%DomainNetBIOSName%\\Exchange Trusted Subsystem" 28 | }, 29 | { 30 | "Path": "OU=Microsoft Exchange Security Groups,%DomainDN%", 31 | "ActiveDirectoryRights": "WriteProperty", 32 | "InheritanceType": "Descendents", 33 | "ObjectType": "Member", 34 | "InheritedObjectType": "Group", 35 | "AccessControlType": "Allow", 36 | "Identity": "%DomainNetBIOSName%\\Exchange Trusted Subsystem" 37 | } 38 | ] -------------------------------------------------------------------------------- /ADMF/internal/data/exchangeSPDefaults/accessRules/msExchangeSecurityGroups.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "Path": "OU=Microsoft Exchange Security Groups,%DomainDN%", 4 | "ActiveDirectoryRights": "GenericAll", 5 | "InheritanceType": "All", 6 | "ObjectType": "", 7 | "InheritedObjectType": "", 8 | "AccessControlType": "Allow", 9 | "Identity": "%DomainNetBIOSName%\\Organization Management" 10 | }, 11 | { 12 | "Path": "OU=Microsoft Exchange Security Groups,%DomainDN%", 13 | "ActiveDirectoryRights": "Delete", 14 | "InheritanceType": "Descendents", 15 | "ObjectType": "", 16 | "InheritedObjectType": "Group", 17 | "AccessControlType": "Allow", 18 | "Identity": "%DomainNetBIOSName%\\Exchange Trusted Subsystem" 19 | }, 20 | { 21 | "Path": "OU=Microsoft Exchange Security Groups,%DomainDN%", 22 | "ActiveDirectoryRights": "CreateChild", 23 | "InheritanceType": "All", 24 | "ObjectType": "Group", 25 | "InheritedObjectType": "", 26 | "AccessControlType": "Allow", 27 | "Identity": "%DomainNetBIOSName%\\Exchange Trusted Subsystem" 28 | }, 29 | { 30 | "Path": "OU=Microsoft Exchange Security Groups,%DomainDN%", 31 | "ActiveDirectoryRights": "WriteProperty", 32 | "InheritanceType": "Descendents", 33 | "ObjectType": "Member", 34 | "InheritedObjectType": "Group", 35 | "AccessControlType": "Allow", 36 | "Identity": "%DomainNetBIOSName%\\Exchange Trusted Subsystem" 37 | } 38 | ] -------------------------------------------------------------------------------- /ADMF/internal/components/ExchangeDefaults/domain/accessRules/msExchangeSecurityGroups.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "Path": "OU=Microsoft Exchange Security Groups,%DomainDN%", 4 | "ActiveDirectoryRights": "GenericAll", 5 | "InheritanceType": "All", 6 | "ObjectType": "", 7 | "InheritedObjectType": "", 8 | "AccessControlType": "Allow", 9 | "Identity": "%DomainNetBIOSName%\\Organization Management" 10 | }, 11 | { 12 | "Path": "OU=Microsoft Exchange Security Groups,%DomainDN%", 13 | "ActiveDirectoryRights": "Delete", 14 | "InheritanceType": "Descendents", 15 | "ObjectType": "", 16 | "InheritedObjectType": "Group", 17 | "AccessControlType": "Allow", 18 | "Identity": "%DomainNetBIOSName%\\Exchange Trusted Subsystem" 19 | }, 20 | { 21 | "Path": "OU=Microsoft Exchange Security Groups,%DomainDN%", 22 | "ActiveDirectoryRights": "CreateChild", 23 | "InheritanceType": "All", 24 | "ObjectType": "Group", 25 | "InheritedObjectType": "", 26 | "AccessControlType": "Allow", 27 | "Identity": "%DomainNetBIOSName%\\Exchange Trusted Subsystem" 28 | }, 29 | { 30 | "Path": "OU=Microsoft Exchange Security Groups,%DomainDN%", 31 | "ActiveDirectoryRights": "WriteProperty", 32 | "InheritanceType": "Descendents", 33 | "ObjectType": "Member", 34 | "InheritedObjectType": "Group", 35 | "AccessControlType": "Allow", 36 | "Identity": "%DomainNetBIOSName%\\Exchange Trusted Subsystem" 37 | } 38 | ] -------------------------------------------------------------------------------- /ADMF/internal/components/ExchangeSPDefaults/domain/accessRules/msExchangeSecurityGroups.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "Path": "OU=Microsoft Exchange Security Groups,%DomainDN%", 4 | "ActiveDirectoryRights": "GenericAll", 5 | "InheritanceType": "All", 6 | "ObjectType": "", 7 | "InheritedObjectType": "", 8 | "AccessControlType": "Allow", 9 | "Identity": "%DomainNetBIOSName%\\Organization Management" 10 | }, 11 | { 12 | "Path": "OU=Microsoft Exchange Security Groups,%DomainDN%", 13 | "ActiveDirectoryRights": "Delete", 14 | "InheritanceType": "Descendents", 15 | "ObjectType": "", 16 | "InheritedObjectType": "Group", 17 | "AccessControlType": "Allow", 18 | "Identity": "%DomainNetBIOSName%\\Exchange Trusted Subsystem" 19 | }, 20 | { 21 | "Path": "OU=Microsoft Exchange Security Groups,%DomainDN%", 22 | "ActiveDirectoryRights": "CreateChild", 23 | "InheritanceType": "All", 24 | "ObjectType": "Group", 25 | "InheritedObjectType": "", 26 | "AccessControlType": "Allow", 27 | "Identity": "%DomainNetBIOSName%\\Exchange Trusted Subsystem" 28 | }, 29 | { 30 | "Path": "OU=Microsoft Exchange Security Groups,%DomainDN%", 31 | "ActiveDirectoryRights": "WriteProperty", 32 | "InheritanceType": "Descendents", 33 | "ObjectType": "Member", 34 | "InheritedObjectType": "Group", 35 | "AccessControlType": "Allow", 36 | "Identity": "%DomainNetBIOSName%\\Exchange Trusted Subsystem" 37 | } 38 | ] -------------------------------------------------------------------------------- /ADMF/internal/data/domainDefaults/accessRules/addefault_cn_Infrastructure.json: -------------------------------------------------------------------------------- 1 | [ 2 | 3 | { 4 | "Path": "CN=Infrastructure,%DomainDN%", 5 | "ActiveDirectoryRights": "GenericRead", 6 | "InheritanceType": "None", 7 | "ObjectType": "00000000-0000-0000-0000-000000000000", 8 | "InheritedObjectType": "00000000-0000-0000-0000-000000000000", 9 | "AccessControlType": "Allow", 10 | "Identity": "NT AUTHORITY\\Authenticated Users" 11 | }, 12 | { 13 | "Path": "CN=Infrastructure,%DomainDN%", 14 | "ActiveDirectoryRights": "GenericAll", 15 | "InheritanceType": "None", 16 | "ObjectType": "00000000-0000-0000-0000-000000000000", 17 | "InheritedObjectType": "00000000-0000-0000-0000-000000000000", 18 | "AccessControlType": "Allow", 19 | "Identity": "NT AUTHORITY\\SYSTEM" 20 | }, 21 | { 22 | "Path": "CN=Infrastructure,%DomainDN%", 23 | "ActiveDirectoryRights": "268435456", 24 | "InheritanceType": "None", 25 | "ObjectType": "00000000-0000-0000-0000-000000000000", 26 | "InheritedObjectType": "00000000-0000-0000-0000-000000000000", 27 | "AccessControlType": "Allow", 28 | "Identity": "NT AUTHORITY\\SYSTEM", 29 | "Present": "false" 30 | }, 31 | { 32 | "Path": "CN=Infrastructure,%DomainDN%", 33 | "ActiveDirectoryRights": "CreateChild, Self, WriteProperty, ExtendedRight, GenericRead, WriteDacl, WriteOwner", 34 | "InheritanceType": "None", 35 | "ObjectType": "00000000-0000-0000-0000-000000000000", 36 | "InheritedObjectType": "00000000-0000-0000-0000-000000000000", 37 | "AccessControlType": "Allow", 38 | "Identity": "%DomainName%\\Domain Admins" 39 | } 40 | ] -------------------------------------------------------------------------------- /ADMF/internal/components/DefaultAccessRules/domain/accessRules/addefault_cn_Infrastructure.json: -------------------------------------------------------------------------------- 1 | [ 2 | 3 | { 4 | "Path": "CN=Infrastructure,%DomainDN%", 5 | "ActiveDirectoryRights": "GenericRead", 6 | "InheritanceType": "None", 7 | "ObjectType": "00000000-0000-0000-0000-000000000000", 8 | "InheritedObjectType": "00000000-0000-0000-0000-000000000000", 9 | "AccessControlType": "Allow", 10 | "Identity": "NT AUTHORITY\\Authenticated Users" 11 | }, 12 | { 13 | "Path": "CN=Infrastructure,%DomainDN%", 14 | "ActiveDirectoryRights": "GenericAll", 15 | "InheritanceType": "None", 16 | "ObjectType": "00000000-0000-0000-0000-000000000000", 17 | "InheritedObjectType": "00000000-0000-0000-0000-000000000000", 18 | "AccessControlType": "Allow", 19 | "Identity": "NT AUTHORITY\\SYSTEM" 20 | }, 21 | { 22 | "Path": "CN=Infrastructure,%DomainDN%", 23 | "ActiveDirectoryRights": "268435456", 24 | "InheritanceType": "None", 25 | "ObjectType": "00000000-0000-0000-0000-000000000000", 26 | "InheritedObjectType": "00000000-0000-0000-0000-000000000000", 27 | "AccessControlType": "Allow", 28 | "Identity": "NT AUTHORITY\\SYSTEM", 29 | "Present": "false" 30 | }, 31 | { 32 | "Path": "CN=Infrastructure,%DomainDN%", 33 | "ActiveDirectoryRights": "CreateChild, Self, WriteProperty, ExtendedRight, GenericRead, WriteDacl, WriteOwner", 34 | "InheritanceType": "None", 35 | "ObjectType": "00000000-0000-0000-0000-000000000000", 36 | "InheritedObjectType": "00000000-0000-0000-0000-000000000000", 37 | "AccessControlType": "Allow", 38 | "Identity": "%DomainName%\\Domain Admins" 39 | } 40 | ] -------------------------------------------------------------------------------- /ADMF/internal/data/context/domain/gppermissions/readme.md: -------------------------------------------------------------------------------- 1 | # Group Policy Permissions 2 | 3 | ## Synopsis 4 | 5 | Define permissions that should apply to group policy objects. 6 | 7 | Drop any number of json files in this folder. 8 | Each json file can contain one or several configuration items. 9 | Each configuration setting must map to the parameters of Register-DMGPPermission in one valid parameterset. 10 | Different items in the same file need not all be of the same parameterset. 11 | 12 | > Note on filters 13 | 14 | Filters can be a filter string that would be legal powershell, however they can contain only a few items: 15 | 16 | + Parenthesis 17 | + Logical operators (-and, -or, -xor, -not) 18 | + Filter names (either as if they were a command or a an argument) 19 | 20 | They must be syntactically correct as far as PowerShell is concerned. 21 | 22 | For example, these would be all be legal filters: 23 | 24 | + `IsManaged -and Tier0` 25 | + `-not (IsManaged) -or (Tier1 -and UserScope)` 26 | 27 | All filters must be defined when testing GP Permissions. 28 | To define a filter, see the `gppermissionfilters` Component. 29 | 30 | Were a filter string accepted where a filter condition has not been defined, we could not be sure the permissions were correctly resolved, which might lead to critical security configuration errors. 31 | Due to this any configuration error will lead to terminating the test and not providing any actions! 32 | 33 | Group Policy Permissions and their filters need not be defined in the same Context, but they _will_ be evaluated at test time. 34 | 35 | All filter conditions currently supported come down to these distinct types: 36 | 37 | + Is the GPO defined in the currently loaded configuration? 38 | + Is the GPO linked to this specific OU, or anywhere below it, or to any OU directly below it? 39 | + Is the GPO named like this? (Exact comparison / Wildcard / Regex) 40 | 41 | The name chosen for a condition is arbitrary, but can only contain letters, numbers and underscore. 42 | -------------------------------------------------------------------------------- /ADMF/internal/data/domainDefaults/gppermissions/readme.md: -------------------------------------------------------------------------------- 1 | # Group Policy Permissions 2 | 3 | ## Synopsis 4 | 5 | Define permissions that should apply to group policy objects. 6 | 7 | Drop any number of json files in this folder. 8 | Each json file can contain one or several configuration items. 9 | Each configuration setting must map to the parameters of Register-DMGPPermission in one valid parameterset. 10 | Different items in the same file need not all be of the same parameterset. 11 | 12 | > Note on filters 13 | 14 | Filters can be a filter string that would be legal powershell, however they can contain only a few items: 15 | 16 | + Parenthesis 17 | + Logical operators (-and, -or, -xor, -not) 18 | + Filter names (either as if they were a command or a an argument) 19 | 20 | They must be syntactically correct as far as PowerShell is concerned. 21 | 22 | For example, these would be all be legal filters: 23 | 24 | + `IsManaged -and Tier0` 25 | + `-not (IsManaged) -or (Tier1 -and UserScope)` 26 | 27 | All filters must be defined when testing GP Permissions. 28 | To define a filter, see the `gppermissionfilters` Component. 29 | 30 | Were a filter string accepted where a filter condition has not been defined, we could not be sure the permissions were correctly resolved, which might lead to critical security configuration errors. 31 | Due to this any configuration error will lead to terminating the test and not providing any actions! 32 | 33 | Group Policy Permissions and their filters need not be defined in the same Context, but they _will_ be evaluated at test time. 34 | 35 | All filter conditions currently supported come down to these distinct types: 36 | 37 | + Is the GPO defined in the currently loaded configuration? 38 | + Is the GPO linked to this specific OU, or anywhere below it, or to any OU directly below it? 39 | + Is the GPO named like this? (Exact comparison / Wildcard / Regex) 40 | 41 | The name chosen for a condition is arbitrary, but can only contain letters, numbers and underscore. 42 | -------------------------------------------------------------------------------- /ADMF/internal/components/DefaultAccessRules/domain/gppermissions/readme.md: -------------------------------------------------------------------------------- 1 | # Group Policy Permissions 2 | 3 | ## Synopsis 4 | 5 | Define permissions that should apply to group policy objects. 6 | 7 | Drop any number of json files in this folder. 8 | Each json file can contain one or several configuration items. 9 | Each configuration setting must map to the parameters of Register-DMGPPermission in one valid parameterset. 10 | Different items in the same file need not all be of the same parameterset. 11 | 12 | > Note on filters 13 | 14 | Filters can be a filter string that would be legal powershell, however they can contain only a few items: 15 | 16 | + Parenthesis 17 | + Logical operators (-and, -or, -xor, -not) 18 | + Filter names (either as if they were a command or a an argument) 19 | 20 | They must be syntactically correct as far as PowerShell is concerned. 21 | 22 | For example, these would be all be legal filters: 23 | 24 | + `IsManaged -and Tier0` 25 | + `-not (IsManaged) -or (Tier1 -and UserScope)` 26 | 27 | All filters must be defined when testing GP Permissions. 28 | To define a filter, see the `gppermissionfilters` Component. 29 | 30 | Were a filter string accepted where a filter condition has not been defined, we could not be sure the permissions were correctly resolved, which might lead to critical security configuration errors. 31 | Due to this any configuration error will lead to terminating the test and not providing any actions! 32 | 33 | Group Policy Permissions and their filters need not be defined in the same Context, but they _will_ be evaluated at test time. 34 | 35 | All filter conditions currently supported come down to these distinct types: 36 | 37 | + Is the GPO defined in the currently loaded configuration? 38 | + Is the GPO linked to this specific OU, or anywhere below it, or to any OU directly below it? 39 | + Is the GPO named like this? (Exact comparison / Wildcard / Regex) 40 | 41 | The name chosen for a condition is arbitrary, but can only contain letters, numbers and underscore. 42 | -------------------------------------------------------------------------------- /ADMF/internal/scripts/initialize.ps1: -------------------------------------------------------------------------------- 1 | $callbackScript = { 2 | [CmdletBinding()] 3 | param ( 4 | [AllowNull()] 5 | $Server, 6 | 7 | [AllowNull()] 8 | $Credential, 9 | 10 | [AllowNull()] 11 | $ForestObject, 12 | 13 | [AllowNull()] 14 | $DomainObject 15 | ) 16 | 17 | $parameters = $PSBoundParameters | ConvertTo-PSFHashtable -Include Server, Credential 18 | if ($parameters.Server -eq '') { $parameters.Server = $env:USERDNSDOMAIN } 19 | Set-AdmfContext @parameters -Interactive -ReUse -EnableException 20 | } 21 | Register-DMCallback -Name ADMF -ScriptBlock $callbackScript 22 | Register-FMCallback -Name ADMF -ScriptBlock $callbackScript 23 | 24 | $callbackScript2 = { 25 | [CmdletBinding()] 26 | param ( 27 | [Hashtable] 28 | $Data 29 | ) 30 | 31 | # If this is a DC Installation command from DC Management and we disabled the prompt in configuration, stop 32 | if ($Data.Data.IsDCInstall -and -not (Get-PSFConfigValue -FullName 'ADMF.DCInstall.Context.Prompt.Enable')) { return } 33 | 34 | $parameters = $Data.Data | ConvertTo-PSFHashtable -Include Server, Credential 35 | if ($parameters.Server -eq '') { $parameters.Server = $env:USERDNSDOMAIN } 36 | if (-not $parameters.Server) { $parameters.Server = $env:USERDNSDOMAIN } 37 | Set-AdmfContext @parameters -Interactive -ReUse -EnableException -NoDomain:($Data.Data.IsDCInstall -as [bool]) 38 | } 39 | Register-PSFCallback -Name 'ADMF.ContextPrompt' -ModuleName DCManagement -CommandName '*' -ScriptBlock $callbackScript2 40 | 41 | Set-PSFTypeAlias -Mapping @{ 42 | 'UpdateDCOptions' = 'ADMF.UpdateDCOptions' 43 | 'UpdateDomainOptions' = 'ADMF.UpdateDomainOptions' 44 | 'UpdateForestOptions' = 'ADMF.UpdateForestOptions' 45 | } 46 | 47 | Register-AdmfCredentialProvider -Name default -PreScript { 48 | param ( 49 | $Data 50 | ) 51 | $Data.Credential 52 | } 53 | 54 | Set-PSFFeature -Name PSFramework.Stop-PSFFunction.ShowWarning -Value $true -ModuleName ADMF 55 | 56 | $null = Get-Acl -Path $HOME -ErrorAction Ignore -------------------------------------------------------------------------------- /azFunctionResources/clientModule/internal/functions/Get-InternalConnectionData.ps1: -------------------------------------------------------------------------------- 1 | function Get-InternalConnectionData 2 | { 3 | <# 4 | .SYNOPSIS 5 | Creates parameter hashtables for Invoke-RestMethod calls. 6 | 7 | .DESCRIPTION 8 | Creates parameter hashtables for Invoke-RestMethod calls. 9 | This is the main abstraction layer for public functions. 10 | 11 | .PARAMETER Method 12 | The Rest Method to use when calling this function. 13 | 14 | .PARAMETER Parameters 15 | The PSBoundParameters object. Will be passed online using PowerShell Serialization. 16 | 17 | .PARAMETER FunctionName 18 | The name of the Azure Function to call. 19 | This should always be the condensed name of the function. 20 | #> 21 | [CmdletBinding()] 22 | param ( 23 | [string] 24 | $Method, 25 | 26 | $Parameters, 27 | 28 | [string] 29 | $FunctionName 30 | ) 31 | 32 | process 33 | { 34 | try { $uri = '{0}{1}' -f (Get-PSFConfigValue -FullName 'ADMF.Client.Uri' -NotNull), $FunctionName } 35 | catch { $PSCmdlet.ThrowTerminatingError($_) } 36 | $header = @{ } 37 | 38 | #region Authentication 39 | $unprotectedToken = Get-PSFConfigValue -FullName 'ADMF.Client.UnprotectedToken' 40 | $protectedToken = Get-PSFConfigValue -FullName 'ADMF.Client.ProtectedToken' 41 | 42 | $authenticationDone = $false 43 | if ($protectedToken -and -not $authenticationDone) 44 | { 45 | $uri += '?code={0}' -f $protectedToken.GetNetworkCredential().Password 46 | $authenticationDone = $true 47 | } 48 | if ($unprotectedToken -and -not $authenticationDone) 49 | { 50 | $uri += '?code={0}' -f $unprotectedToken 51 | $authenticationDone = $true 52 | } 53 | if (-not $authenticationDone) 54 | { 55 | throw "No Authentication configured!" 56 | } 57 | #endregion Authentication 58 | 59 | 60 | @{ 61 | Method = $Method 62 | Uri = $uri 63 | Headers = $header 64 | Body = (@{ 65 | __SerializedParameters = ($Parameters | ConvertTo-PSFHashtable | ConvertTo-PSFClixml) 66 | __PSSerialize = $true 67 | } | ConvertTo-Json) 68 | } 69 | } 70 | } -------------------------------------------------------------------------------- /ADMF/internal/functions/Invoke-PostCredentialProvider.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-PostCredentialProvider { 2 | <# 3 | .SYNOPSIS 4 | Executes the PostScript action of a credential provider. 5 | 6 | .DESCRIPTION 7 | Executes the PostScript action of a credential provider. 8 | 9 | .PARAMETER ProviderName 10 | Name of the credential provider to use. 11 | 12 | .PARAMETER Server 13 | The original server targeted. 14 | 15 | .PARAMETER Credential 16 | The original credentials specified by the user. 17 | 18 | .PARAMETER Cmdlet 19 | The $PSCmdlet object of the calling command. 20 | Used to kill it with maximum prejudice in case of error. 21 | 22 | .EXAMPLE 23 | PS C:\> Invoke-PostCredentialProvider -ProviderName $CredentialProvider -Server $originalArgument.Server -Credential $originalArgument.Credential 24 | 25 | Performs any post-execution action registered for the $CredentialProvider (if any) 26 | #> 27 | [CmdletBinding()] 28 | param ( 29 | [string] 30 | $ProviderName, 31 | 32 | [PSFComputer] 33 | $Server, 34 | 35 | [AllowNull()] 36 | [PSCredential] 37 | $Credential, 38 | 39 | [System.Management.Automation.PSCmdlet] 40 | $Cmdlet = $PSCmdlet 41 | ) 42 | 43 | if (-not $script:credentialProviders[$ProviderName]) { 44 | Stop-PSFFunction -String 'Invoke-PostCredentialProvider.Provider.NotFound' -StringValues $ProviderName -EnableException $true -Cmdlet $Cmdlet 45 | } 46 | 47 | if (-not $script:credentialProviders[$ProviderName].PostScript) { return } 48 | 49 | $argument = [PSCustomObject]@{ 50 | Server = $Server 51 | Credential = $Credential 52 | } 53 | 54 | try { $null = $script:credentialProviders[$ProviderName].PostScript.Invoke($argument) } 55 | catch { 56 | Stop-PSFFunction -String 'Invoke-PostCredentialProvider.Provider.ExecutionError' -StringValues $ProviderName -EnableException $true -ErrorRecord $_ -Cmdlet $Cmdlet 57 | } 58 | } -------------------------------------------------------------------------------- /ADMF/internal/data/context/dc/shares/readme.md: -------------------------------------------------------------------------------- 1 | # Shares 2 | 3 | ## Description 4 | 5 | Domain controllers might occasionally need extra network shares for some workflows. 6 | If that is the case however, it becomes especially important to consider the security aspects of it. 7 | This is where this Component comes in, as it allows you to: 8 | 9 | - Define network shares 10 | - Define network share permissions 11 | 12 | ## Example Configuration 13 | 14 | A simple share could look like this: 15 | 16 | ```json 17 | { 18 | "Name": "Scripts$", 19 | "Path": "C:\\Scripts", 20 | "Description": "Backup location for all Tier 0 admin code. Signed code only.", 21 | "FullAccess": [ "%DomainName%\\Domain Admins" ], 22 | "WriteAccess": [ "%DomainName%\\Tier 0 Admins" ], 23 | "ReadAccess": [ "%DomainName%\\Tier 0 Operators" ] 24 | } 25 | ``` 26 | 27 | ## Parameters 28 | 29 | ### Name 30 | 31 | The name of the share. 32 | 33 | > Supports string resolution. 34 | 35 | ### Path 36 | 37 | The path the share points to. 38 | 39 | > Supports string resolution. 40 | 41 | ### Description 42 | 43 | The description of the share. 44 | 45 | > Supports string resolution. 46 | 47 | ### FullAccess 48 | 49 | The principals to grant full access to. 50 | 51 | > Supports string resolution. 52 | 53 | ### WriteAccess 54 | 55 | The principals to grant write access to. 56 | 57 | > Supports string resolution. 58 | 59 | ### ReadAccess 60 | 61 | The principals to grant read access to. 62 | 63 | > Supports string resolution. 64 | 65 | ### AccessMode 66 | 67 | How share access rules are processed. 68 | Supports three configurations: 69 | 70 | - Constrained: The default access mode, will remove any excess access rules. 71 | - Additive: Ignore any access rules already on the share, even if not configured 72 | - Defined: Ignore any access rules already on the share, even if not configured UNLESS the identity on those rules has an access level defined for it. 73 | 74 | ### ServerRole 75 | 76 | What domain controller to apply this to: 77 | 78 | - All: All DCs in the enterprise 79 | - FSMO: Only DCs that have any FSMO role 80 | - PDC: Only the PDCEmulator 81 | -------------------------------------------------------------------------------- /azFunctionResources/clientModule/functions/Connect-ADMF.ps1: -------------------------------------------------------------------------------- 1 | function Connect-ADMF 2 | { 3 | <# 4 | .SYNOPSIS 5 | Configures the connection to the ADMF Azure Function. 6 | 7 | .DESCRIPTION 8 | Configures the connection to the ADMF Azure Function. 9 | 10 | .PARAMETER Uri 11 | Url to connect to the ADMF Azure function. 12 | 13 | .PARAMETER UnprotectedToken 14 | The unencrypted access token to the ADMF Azure function. ONLY use this from secure locations or non-sensitive functions! 15 | 16 | .PARAMETER ProtectedToken 17 | An encrypted access token to the ADMF Azure function. Use this to persist an access token in a way only the current user on the current system can access. 18 | 19 | .PARAMETER Register 20 | Using this command, the module will remember the connection settings persistently across PowerShell sessions. 21 | CAUTION: When using unencrypted token data (such as specified through the -UnprotectedToken parameter), the authenticating token will be stored in clear-text! 22 | 23 | .EXAMPLE 24 | PS C:\> Connect-ADMF -Uri 'https://demofunctionapp.azurewebsites.net/api/' 25 | 26 | Establishes a connection to ADMF 27 | #> 28 | [CmdletBinding()] 29 | param ( 30 | [string] 31 | $Uri, 32 | 33 | [string] 34 | $UnprotectedToken, 35 | 36 | [System.Management.Automation.PSCredential] 37 | $ProtectedToken, 38 | 39 | [switch] 40 | $Register 41 | ) 42 | 43 | process 44 | { 45 | if (Test-PSFParameterBinding -ParameterName UnprotectedToken) 46 | { 47 | Set-PSFConfig -Module 'ADMF' -Name 'Client.UnprotectedToken' -Value $UnprotectedToken 48 | if ($Register) { Register-PSFConfig -Module 'ADMF' -Name 'Client.UnprotectedToken' } 49 | } 50 | if (Test-PSFParameterBinding -ParameterName Uri) 51 | { 52 | Set-PSFConfig -Module 'ADMF' -Name 'Client.Uri' -Value $Uri 53 | if ($Register) { Register-PSFConfig -Module 'ADMF' -Name 'Client.Uri' } 54 | } 55 | if (Test-PSFParameterBinding -ParameterName ProtectedToken) 56 | { 57 | Set-PSFConfig -Module 'ADMF' -Name 'Client.ProtectedToken' -Value $ProtectedToken 58 | if ($Register) { Register-PSFConfig -Module 'ADMF' -Name 'Client.ProtectedToken' } 59 | } 60 | 61 | } 62 | } -------------------------------------------------------------------------------- /ADMF/internal/data/context/domain/domaindata/readme.md: -------------------------------------------------------------------------------- 1 | # Domain Data 2 | 3 | ## Synopsis 4 | 5 | The Context system is by itself not domain specific. 6 | That is, applying the same Context to different domains should bring the same results. 7 | 8 | It can automatically adjust for aspects such as domain name or distinguished names, but beyond that, it is not affected by the domain. 9 | This means that switching targeted domain will not reload the Context, if the next domain has the same context! 10 | 11 | However, sometimes we may want to be able to execute custom logic against a specific domain. 12 | Instead of requiring a custom per-domain Context, the Domain Data system allows providing data using custom script logic that is executed on-invocation against the targeted domain. 13 | The resulting data can then be further used in other Components. 14 | Support for that is still rare, but will be made available to a larger scale of Components as time permits. 15 | 16 | Currently implementing Components: 17 | 18 | + Group Policy Registry Settings 19 | 20 | ## Defining Domain Data 21 | 22 | To define domain data, provide any number of `psd1` files in this folder (NOT `json`!). 23 | Each psd1 file needs to contain one or more hashtables with two keys: 24 | 25 | + Name 26 | + Scriptblock 27 | 28 | Example: 29 | 30 | ```powershell 31 | @{ 32 | Name = 'DomainGUID' 33 | Scriptblock = { 34 | param ( 35 | $Parameters 36 | ) 37 | (Get-ADDomain @parameters).ObjectGUID 38 | } 39 | } 40 | ``` 41 | 42 | ## Properties 43 | 44 | ### Name 45 | 46 | The name can be any string, so long as it contains only letters, numbers or underscores. 47 | The name is _not_ case sensitive! 48 | 49 | ### Scriptblock 50 | 51 | The Scriptblock is a PowerShell scriptblock, receiving a single parameter - a hashtable containing the target domain (`Server` ; or domain controller) and - if specified - credentials to use ( `Credential`). 52 | 53 | It should then proceed to gather data, do whatever it is intended to do, and finally return the result. 54 | If the script throws an exception, the ADMF is going to assume the worst and refuse to process the corresponding Component, lest it apply an inconsistent, erroneous state. 55 | -------------------------------------------------------------------------------- /ADMF/functions/New-AdmfContextStore.ps1: -------------------------------------------------------------------------------- 1 | function New-AdmfContextStore 2 | { 3 | <# 4 | .SYNOPSIS 5 | Creates a new Context Store. 6 | 7 | .DESCRIPTION 8 | Creates a new Context Store. 9 | Context Stores are locations where configuration contexts are stored and retrieved from. 10 | 11 | These contexts are stored using the PSFramework configuration system: 12 | https://psframework.org/documentation/documents/psframework/configuration.html 13 | Making it possible to deploy them using GPO, SCCM or other computer or profile management solutions. 14 | 15 | .PARAMETER Name 16 | The name of the store to create. 17 | Must not exist yet. 18 | 19 | .PARAMETER Path 20 | The path where the context is pointing at. 21 | Must be an existing folder. 22 | 23 | .PARAMETER Scope 24 | Where to persist the store. 25 | by default, this is stored in HKCU, making the store persistently available to the user. 26 | For more information on scopes, and what location they corespond with, see: 27 | https://psframework.org/documentation/documents/psframework/configuration/persistence-location.html 28 | 29 | .PARAMETER EnableException 30 | This parameters disables user-friendly warnings and enables the throwing of exceptions. 31 | This is less user friendly, but allows catching exceptions in calling scripts. 32 | 33 | .EXAMPLE 34 | PS C:\> New-AdmfContextStore -Name 'company' -Path '\\contoso\system\ad\contexts' 35 | 36 | Creates a new context named 'company', pointing at '\\contoso\system\ad\contexts' 37 | #> 38 | [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "")] 39 | [CmdletBinding()] 40 | param ( 41 | [Parameter(Mandatory = $true)] 42 | [PsfValidateScript('ADMF.Validate.ContextStore.ExistsNot', ErrorString = 'ADMF.Validate.ContextStore.ExistsNot')] 43 | [PsfValidatePattern('^[\w\d_\-\.]+$', ErrorString = 'ADMF.Validate.Pattern.ContextStoreName')] 44 | [string] 45 | $Name, 46 | 47 | [Parameter(Mandatory = $true)] 48 | [PsfValidateScript('ADMF.Validate.Path.Folder', ErrorString = 'ADMF.Validate.Path.Folder')] 49 | [string] 50 | $Path, 51 | 52 | [PSFramework.Configuration.ConfigScope] 53 | $Scope = "UserDefault", 54 | 55 | [switch] 56 | $EnableException 57 | ) 58 | 59 | process 60 | { 61 | $resolvedPath = Resolve-PSFPath -Path $Path -Provider FileSystem -SingleItem 62 | Set-PSFConfig -FullName "ADMF.Context.Store.$Name" -Value $resolvedPath 63 | Register-PSFConfig -FullName "ADMF.Context.Store.$Name" -Scope $Scope -EnableException:$EnableException 64 | } 65 | } -------------------------------------------------------------------------------- /ADMF/internal/functions/Invoke-PreCredentialProvider.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-PreCredentialProvider { 2 | <# 3 | .SYNOPSIS 4 | Resolves credentials to use using the registered credential provider. 5 | 6 | .DESCRIPTION 7 | Resolves credentials to use using the registered credential provider. 8 | 9 | .PARAMETER ProviderName 10 | Name of the credential provider to use. 11 | 12 | .PARAMETER Server 13 | The server to connect to. 14 | 15 | .PARAMETER Credential 16 | The credentials specified by the user. 17 | 18 | .PARAMETER Parameter 19 | The parameter object resolved from the original user input. 20 | 21 | .PARAMETER Cmdlet 22 | The $PSCmdlet object of the calling command. 23 | Used to kill it with maximum prejudice in case of error. 24 | 25 | .EXAMPLE 26 | PS C:\> $originalArgument = Invoke-PreCredentialProvider @parameters -ProviderName $CredentialProvider -Parameter $parameters 27 | 28 | Resolves the credentials to use and automatically injects them into the $parameters hashtable. 29 | Also returns the original input for use when invoking the PostScript scriptblock of the provider. 30 | #> 31 | [CmdletBinding()] 32 | param ( 33 | [string] 34 | $ProviderName, 35 | 36 | [PSFComputer] 37 | $Server, 38 | 39 | [AllowNull()] 40 | [PSCredential] 41 | $Credential, 42 | 43 | [Hashtable] 44 | $Parameter, 45 | 46 | [System.Management.Automation.PSCmdlet] 47 | $Cmdlet = $PSCmdlet 48 | ) 49 | 50 | if (-not $script:credentialProviders[$ProviderName]) 51 | { 52 | Stop-PSFFunction -String 'Invoke-PreCredentialProvider.Provider.NotFound' -StringValues $ProviderName -EnableException $true -Cmdlet $Cmdlet 53 | } 54 | 55 | $argument = [PSCustomObject]@{ 56 | Server = $Server 57 | Credential = $Credential 58 | } 59 | 60 | try { $results = $script:credentialProviders[$ProviderName].PreScript.Invoke($argument) | Where-Object { $_ -is [PSCredential] } | Select-Object -First 1 } 61 | catch 62 | { 63 | Stop-PSFFunction -String 'Invoke-PreCredentialProvider.Provider.ExecutionError' -StringValues $ProviderName -EnableException $true -ErrorRecord $_ -Cmdlet $Cmdlet 64 | } 65 | 66 | if ($results) { 67 | $Parameter['Credential'] = $results 68 | } 69 | elseif ($Parameter.ContainsKey('Credential')) { $Parameter.Remove('Credential') } 70 | 71 | return $argument 72 | } -------------------------------------------------------------------------------- /ADMF/internal/data/context/forest/exchangeschema/readme.md: -------------------------------------------------------------------------------- 1 | # Exchange Schema 2 | 3 | ## Description 4 | 5 | This component uses an Exchange Server ISO image to apply Exchange Schema updates or upgrade a forest's Exchange topology for a given Exchange level. 6 | 7 | In order to apply these settings in a supported manner, using the official exchange installer is required. 8 | There is no validation, whether the provided ISO image is actually of the required version! 9 | 10 | > At this time, the ISO must somehow be provided on the target DC. 11 | This solution does not - yet - support copying it as part of the operation. 12 | 13 | ## Example Configuration 14 | 15 | Configure schema updates only to Level of Exchange 2019 CU6: 16 | 17 | ```json 18 | { 19 | "LocalImagePath": "C:\\images\\exchange_2019CU6.iso", 20 | "ExchangeVersion": "2019CU6", 21 | "SchemaOnly": true 22 | } 23 | ``` 24 | 25 | Configure Schema and Configuration level changes for Exchange 2019 CU6: 26 | 27 | ```json 28 | { 29 | "LocalImagePath": "C:\\images\\exchange_2019CU6.iso", 30 | "ExchangeVersion": "2019CU6", 31 | "OrganizationName": "Contoso" 32 | } 33 | ``` 34 | 35 | > Note: The exchange installer will generally also prepare the root domain as part of the update. 36 | 37 | ## Parameters 38 | 39 | ### LocalImagePath 40 | 41 | The path where to find the Exchange ISO file 42 | Must be local on the remote server connected to! 43 | Updating the Exchange AD settings is only supported when executed through the installer contained in that ISO file without exceptions. 44 | 45 | ### ExchangeVersion 46 | 47 | The version of the Exchange server to apply. 48 | E.g. 2016CU6 49 | We map Exchange versions to their respective identifiers in AD: 50 | RangeUpper in schema and ObjectVersion in configuration. 51 | This parameter is to help avoiding to have to look up those values. 52 | If your version is not supported by us yet, look up those numbers and explicitly bind it to -RangeUpper and -ObjectVersion instead. 53 | 54 | ### RangeUpper 55 | 56 | The explicit RangeUpper schema attribute property, found on the ms-Exch-Schema-Version-Pt class in schema. 57 | 58 | ### ObjectVersion 59 | 60 | The object version on the msExchOrganizationContainer type object in the configuration. 61 | Do NOT confuse that with the ObjectVersion of the exchange object in the default Naming Context (regular domain space). 62 | 63 | ### OrganizationName 64 | 65 | The name of the Exchange Organization. 66 | Only used for CREATING a new Exchange deployment. 67 | Make sure to customize this if you are picky about names like that. 68 | 69 | ### SchemaOnly 70 | 71 | Whether to only apply the schema updates. 72 | Enabling this will mean no configuration scope changes are applied and the root domain also will not be pre-configured for Exchange. 73 | -------------------------------------------------------------------------------- /ADMF/tests/general/Manifest.Tests.ps1: -------------------------------------------------------------------------------- 1 | Describe "Validating the module manifest" { 2 | $moduleRoot = (Resolve-Path "$global:testroot\..").Path 3 | $manifest = ((Get-Content "$moduleRoot\ADMF.psd1") -join "`n") | Invoke-Expression 4 | Context "Basic resources validation" { 5 | $files = Get-ChildItem "$moduleRoot\functions" -Recurse -File | Where-Object Name -like "*.ps1" 6 | It "Exports all functions in the public folder" -TestCases @{ files = $files; manifest = $manifest } { 7 | 8 | $functions = (Compare-Object -ReferenceObject $files.BaseName -DifferenceObject $manifest.FunctionsToExport | Where-Object SideIndicator -Like '<=').InputObject 9 | $functions | Should -BeNullOrEmpty 10 | } 11 | It "Exports no function that isn't also present in the public folder" -TestCases @{ files = $files; manifest = $manifest } { 12 | $functions = (Compare-Object -ReferenceObject $files.BaseName -DifferenceObject $manifest.FunctionsToExport | Where-Object SideIndicator -Like '=>').InputObject 13 | $functions | Should -BeNullOrEmpty 14 | } 15 | 16 | It "Exports none of its internal functions" -TestCases @{ moduleRoot = $moduleRoot; manifest = $manifest } { 17 | $files = Get-ChildItem "$moduleRoot\internal\functions" -Recurse -File -Filter "*.ps1" 18 | $files | Where-Object BaseName -In $manifest.FunctionsToExport | Should -BeNullOrEmpty 19 | } 20 | } 21 | 22 | Context "Individual file validation" { 23 | It "The root module file exists" -TestCases @{ moduleRoot = $moduleRoot; manifest = $manifest } { 24 | Test-Path "$moduleRoot\$($manifest.RootModule)" | Should -Be $true 25 | } 26 | 27 | foreach ($format in $manifest.FormatsToProcess) 28 | { 29 | It "The file $format should exist" -TestCases @{ moduleRoot = $moduleRoot; format = $format } { 30 | Test-Path "$moduleRoot\$format" | Should -Be $true 31 | } 32 | } 33 | 34 | foreach ($type in $manifest.TypesToProcess) 35 | { 36 | It "The file $type should exist" -TestCases @{ moduleRoot = $moduleRoot; type = $type } { 37 | Test-Path "$moduleRoot\$type" | Should -Be $true 38 | } 39 | } 40 | 41 | foreach ($assembly in $manifest.RequiredAssemblies) 42 | { 43 | if ($assembly -like "*.dll") { 44 | It "The file $assembly should exist" -TestCases @{ moduleRoot = $moduleRoot; assembly = $assembly } { 45 | Test-Path "$moduleRoot\$assembly" | Should -Be $true 46 | } 47 | } 48 | else { 49 | It "The file $assembly should load from the GAC" -TestCases @{ moduleRoot = $moduleRoot; assembly = $assembly } { 50 | { Add-Type -AssemblyName $assembly } | Should -Not -Throw 51 | } 52 | } 53 | } 54 | 55 | foreach ($tag in $manifest.PrivateData.PSData.Tags) 56 | { 57 | It "Tags should have no spaces in name" -TestCases @{ tag = $tag } { 58 | $tag -match " " | Should -Be $false 59 | } 60 | } 61 | } 62 | } -------------------------------------------------------------------------------- /ADMF/internal/data/domainDefaults/accessRules/addefault_cn_Computers.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "Path": "CN=Computers,%DomainDN%", 4 | "ActiveDirectoryRights": "CreateChild, DeleteChild, Self, WriteProperty, ExtendedRight, GenericRead, WriteDacl, WriteOwner", 5 | "InheritanceType": "None", 6 | "ObjectType": "00000000-0000-0000-0000-000000000000", 7 | "InheritedObjectType": "00000000-0000-0000-0000-000000000000", 8 | "AccessControlType": "Allow", 9 | "Identity": "%DomainSID%-512" 10 | }, 11 | { 12 | "Path": "CN=Computers,%DomainDN%", 13 | "ActiveDirectoryRights": "GenericAll", 14 | "InheritanceType": "None", 15 | "ObjectType": "00000000-0000-0000-0000-000000000000", 16 | "InheritedObjectType": "00000000-0000-0000-0000-000000000000", 17 | "AccessControlType": "Allow", 18 | "Identity": "%DomainSID%-512", 19 | "Present": "false" 20 | }, 21 | { 22 | "Path": "CN=Computers,%DomainDN%", 23 | "ActiveDirectoryRights": "CreateChild, DeleteChild", 24 | "InheritanceType": "None", 25 | "ObjectType": "bf967aba-0de6-11d0-a285-00aa003049e2", 26 | "InheritedObjectType": "00000000-0000-0000-0000-000000000000", 27 | "AccessControlType": "Allow", 28 | "Identity": "BUILTIN\\Account Operators" 29 | }, 30 | { 31 | "Path": "CN=Computers,%DomainDN%", 32 | "ActiveDirectoryRights": "CreateChild, DeleteChild", 33 | "InheritanceType": "None", 34 | "ObjectType": "bf967a9c-0de6-11d0-a285-00aa003049e2", 35 | "InheritedObjectType": "00000000-0000-0000-0000-000000000000", 36 | "AccessControlType": "Allow", 37 | "Identity": "BUILTIN\\Account Operators" 38 | }, 39 | { 40 | "Path": "CN=Computers,%DomainDN%", 41 | "ActiveDirectoryRights": "CreateChild, DeleteChild", 42 | "InheritanceType": "None", 43 | "ObjectType": "bf967a86-0de6-11d0-a285-00aa003049e2", 44 | "InheritedObjectType": "00000000-0000-0000-0000-000000000000", 45 | "AccessControlType": "Allow", 46 | "Identity": "BUILTIN\\Account Operators" 47 | }, 48 | { 49 | "Path": "CN=Computers,%DomainDN%", 50 | "ActiveDirectoryRights": "CreateChild, DeleteChild", 51 | "InheritanceType": "None", 52 | "ObjectType": "4828cc14-1437-45bc-9b07-ad6f015e5f28", 53 | "InheritedObjectType": "00000000-0000-0000-0000-000000000000", 54 | "AccessControlType": "Allow", 55 | "Identity": "BUILTIN\\Account Operators" 56 | }, 57 | { 58 | "Path": "CN=Computers,%DomainDN%", 59 | "ActiveDirectoryRights": "CreateChild, DeleteChild", 60 | "InheritanceType": "None", 61 | "ObjectType": "bf967aa8-0de6-11d0-a285-00aa003049e2", 62 | "InheritedObjectType": "00000000-0000-0000-0000-000000000000", 63 | "AccessControlType": "Allow", 64 | "Identity": "BUILTIN\\Print Operators" 65 | } 66 | ] -------------------------------------------------------------------------------- /ADMF/internal/components/DefaultAccessRules/domain/accessRules/addefault_cn_Computers.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "Path": "CN=Computers,%DomainDN%", 4 | "ActiveDirectoryRights": "CreateChild, DeleteChild, Self, WriteProperty, ExtendedRight, GenericRead, WriteDacl, WriteOwner", 5 | "InheritanceType": "None", 6 | "ObjectType": "00000000-0000-0000-0000-000000000000", 7 | "InheritedObjectType": "00000000-0000-0000-0000-000000000000", 8 | "AccessControlType": "Allow", 9 | "Identity": "%DomainSID%-512" 10 | }, 11 | { 12 | "Path": "CN=Computers,%DomainDN%", 13 | "ActiveDirectoryRights": "GenericAll", 14 | "InheritanceType": "None", 15 | "ObjectType": "00000000-0000-0000-0000-000000000000", 16 | "InheritedObjectType": "00000000-0000-0000-0000-000000000000", 17 | "AccessControlType": "Allow", 18 | "Identity": "%DomainSID%-512", 19 | "Present": "false" 20 | }, 21 | { 22 | "Path": "CN=Computers,%DomainDN%", 23 | "ActiveDirectoryRights": "CreateChild, DeleteChild", 24 | "InheritanceType": "None", 25 | "ObjectType": "bf967aba-0de6-11d0-a285-00aa003049e2", 26 | "InheritedObjectType": "00000000-0000-0000-0000-000000000000", 27 | "AccessControlType": "Allow", 28 | "Identity": "BUILTIN\\Account Operators" 29 | }, 30 | { 31 | "Path": "CN=Computers,%DomainDN%", 32 | "ActiveDirectoryRights": "CreateChild, DeleteChild", 33 | "InheritanceType": "None", 34 | "ObjectType": "bf967a9c-0de6-11d0-a285-00aa003049e2", 35 | "InheritedObjectType": "00000000-0000-0000-0000-000000000000", 36 | "AccessControlType": "Allow", 37 | "Identity": "BUILTIN\\Account Operators" 38 | }, 39 | { 40 | "Path": "CN=Computers,%DomainDN%", 41 | "ActiveDirectoryRights": "CreateChild, DeleteChild", 42 | "InheritanceType": "None", 43 | "ObjectType": "bf967a86-0de6-11d0-a285-00aa003049e2", 44 | "InheritedObjectType": "00000000-0000-0000-0000-000000000000", 45 | "AccessControlType": "Allow", 46 | "Identity": "BUILTIN\\Account Operators" 47 | }, 48 | { 49 | "Path": "CN=Computers,%DomainDN%", 50 | "ActiveDirectoryRights": "CreateChild, DeleteChild", 51 | "InheritanceType": "None", 52 | "ObjectType": "4828cc14-1437-45bc-9b07-ad6f015e5f28", 53 | "InheritedObjectType": "00000000-0000-0000-0000-000000000000", 54 | "AccessControlType": "Allow", 55 | "Identity": "BUILTIN\\Account Operators" 56 | }, 57 | { 58 | "Path": "CN=Computers,%DomainDN%", 59 | "ActiveDirectoryRights": "CreateChild, DeleteChild", 60 | "InheritanceType": "None", 61 | "ObjectType": "bf967aa8-0de6-11d0-a285-00aa003049e2", 62 | "InheritedObjectType": "00000000-0000-0000-0000-000000000000", 63 | "AccessControlType": "Allow", 64 | "Identity": "BUILTIN\\Print Operators" 65 | } 66 | ] -------------------------------------------------------------------------------- /ADMF/functions/Register-AdmfCredentialProvider.ps1: -------------------------------------------------------------------------------- 1 | function Register-AdmfCredentialProvider { 2 | <# 3 | .SYNOPSIS 4 | Registers a credential provider used by the ADMF. 5 | 6 | .DESCRIPTION 7 | Registers a credential provider used by the ADMF. 8 | 9 | Credential providers are used for translating the credentials to use for all actions performed against active directory. 10 | For example, the ADMF could be extended to support a password safe solution: 11 | When connecting to a target domain, this provider scriptblock would retrieve the required credentials from a password safe solution. 12 | 13 | A credential provider consists of two scriptblocks: 14 | - A PreScript that is executed before running any commands. It must return either a PSCredential object (if applicable) or $null (if default windows credentials should be used instead). 15 | - A PostScript that is executed after all component commands have been executed. It need not return anything. 16 | 17 | Both scriptblocks receive a single input object, with two properties: 18 | - Server: The computer / domain targeted 19 | - Credential: The credentials originally provided (if any - this may be $null instead!) 20 | 21 | .PARAMETER Name 22 | The name of the credential provider. 23 | Each name must be unique, registering a provider using an existing name overwrites the previous provider. 24 | The provider "default" exists as part of ADMF and will be used if no other is specified. Overriding it allows you to change the default provider intentionally, 25 | but may remove your ability to NOT use any credential transformations, so use with care. 26 | 27 | .PARAMETER PreScript 28 | The script to execute before performing actions, in order to resolve the correct credentials to use. 29 | - If it returns a credential object, this object will be used for authenticating all AD operations (including WinRM against domain controllers!). 30 | - If it returns nothing / only non-credential objects, instead the default windows identity of the user is used. 31 | 32 | .PARAMETER PostScript 33 | This script is executed after performing all actions. 34 | You can use this optional script to perform any cleanup actions if necessary. 35 | 36 | .EXAMPLE 37 | PS C:\> Register-AdmfCredentialProvider -Name AZKeyVault -PreScript $keyVaultScript 38 | 39 | Registers the scriptblock defined in $keyVaultScript as "AZKeyVault" provider. 40 | #> 41 | [CmdletBinding()] 42 | param ( 43 | [Parameter(Mandatory = $true)] 44 | [string] 45 | $Name, 46 | 47 | [Parameter(Mandatory = $true)] 48 | [Scriptblock] 49 | $PreScript, 50 | 51 | [Scriptblock] 52 | $PostScript 53 | ) 54 | 55 | $script:credentialProviders[$Name] = [PSCustomObject]@{ 56 | PSTypeName = 'Admf.CredentialProvider' 57 | Name = $Name 58 | PreScript = $PreScript 59 | PostScript = $PostScript 60 | } 61 | } -------------------------------------------------------------------------------- /ADMF/internal/data/domainDefaults/accessRules/addefault_domainControllers.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "ObjectCategory": "DomainController", 4 | "ActiveDirectoryRights": "WriteProperty", 5 | "InheritanceType": "None", 6 | "ObjectType": "User-Account-Restrictions", 7 | "InheritedObjectType": "", 8 | "AccessControlType": "Allow", 9 | "Identity": "%DomainName%\\Domain Admins" 10 | }, 11 | { 12 | "ObjectCategory": "DomainController", 13 | "ActiveDirectoryRights": "WriteProperty", 14 | "InheritanceType": "None", 15 | "ObjectType": "Display-Name", 16 | "InheritedObjectType": "", 17 | "AccessControlType": "Allow", 18 | "Identity": "%DomainName%\\Domain Admins" 19 | }, 20 | { 21 | "ObjectCategory": "DomainController", 22 | "ActiveDirectoryRights": "Self", 23 | "InheritanceType": "None", 24 | "ObjectType": "Service-Principal-Name", 25 | "InheritedObjectType": "", 26 | "AccessControlType": "Allow", 27 | "Identity": "%DomainName%\\Domain Admins" 28 | }, 29 | { 30 | "ObjectCategory": "DomainController", 31 | "ActiveDirectoryRights": "Self", 32 | "InheritanceType": "None", 33 | "ObjectType": "DNS-Host-Name", 34 | "InheritedObjectType": "", 35 | "AccessControlType": "Allow", 36 | "Identity": "%DomainName%\\Domain Admins" 37 | }, 38 | { 39 | "ObjectCategory": "DomainController", 40 | "ActiveDirectoryRights": "WriteProperty", 41 | "InheritanceType": "None", 42 | "ObjectType": "SAM-Account-Name", 43 | "InheritedObjectType": "", 44 | "AccessControlType": "Allow", 45 | "Identity": "%DomainName%\\Domain Admins" 46 | }, 47 | { 48 | "ObjectCategory": "DomainController", 49 | "ActiveDirectoryRights": "WriteProperty", 50 | "InheritanceType": "None", 51 | "ObjectType": "Description", 52 | "InheritedObjectType": "", 53 | "AccessControlType": "Allow", 54 | "Identity": "%DomainName%\\Domain Admins" 55 | }, 56 | { 57 | "ObjectCategory": "DomainController", 58 | "ActiveDirectoryRights": "WriteProperty", 59 | "InheritanceType": "None", 60 | "ObjectType": "User-Logon", 61 | "InheritedObjectType": "", 62 | "AccessControlType": "Allow", 63 | "Identity": "%DomainName%\\Domain Admins" 64 | }, 65 | { 66 | "ObjectCategory": "msDFSR-LocalSettings", 67 | "ActiveDirectoryRights": "-1", 68 | "InheritanceType": "Descendents", 69 | "ObjectType": "", 70 | "InheritedObjectType": "", 71 | "AccessControlType": "Allow", 72 | "Identity": "" 73 | }, 74 | { 75 | "ObjectCategory": "msDFSR-LocalSettings", 76 | "ActiveDirectoryRights": "GenericAll", 77 | "InheritanceType": "None", 78 | "ObjectType": "", 79 | "InheritedObjectType": "", 80 | "AccessControlType": "Allow", 81 | "Identity": "" 82 | } 83 | ] -------------------------------------------------------------------------------- /ADMF/internal/components/DefaultAccessRules/domain/accessRules/addefault_domainControllers.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "ObjectCategory": "DomainController", 4 | "ActiveDirectoryRights": "WriteProperty", 5 | "InheritanceType": "None", 6 | "ObjectType": "User-Account-Restrictions", 7 | "InheritedObjectType": "", 8 | "AccessControlType": "Allow", 9 | "Identity": "%DomainName%\\Domain Admins" 10 | }, 11 | { 12 | "ObjectCategory": "DomainController", 13 | "ActiveDirectoryRights": "WriteProperty", 14 | "InheritanceType": "None", 15 | "ObjectType": "Display-Name", 16 | "InheritedObjectType": "", 17 | "AccessControlType": "Allow", 18 | "Identity": "%DomainName%\\Domain Admins" 19 | }, 20 | { 21 | "ObjectCategory": "DomainController", 22 | "ActiveDirectoryRights": "Self", 23 | "InheritanceType": "None", 24 | "ObjectType": "Service-Principal-Name", 25 | "InheritedObjectType": "", 26 | "AccessControlType": "Allow", 27 | "Identity": "%DomainName%\\Domain Admins" 28 | }, 29 | { 30 | "ObjectCategory": "DomainController", 31 | "ActiveDirectoryRights": "Self", 32 | "InheritanceType": "None", 33 | "ObjectType": "DNS-Host-Name", 34 | "InheritedObjectType": "", 35 | "AccessControlType": "Allow", 36 | "Identity": "%DomainName%\\Domain Admins" 37 | }, 38 | { 39 | "ObjectCategory": "DomainController", 40 | "ActiveDirectoryRights": "WriteProperty", 41 | "InheritanceType": "None", 42 | "ObjectType": "SAM-Account-Name", 43 | "InheritedObjectType": "", 44 | "AccessControlType": "Allow", 45 | "Identity": "%DomainName%\\Domain Admins" 46 | }, 47 | { 48 | "ObjectCategory": "DomainController", 49 | "ActiveDirectoryRights": "WriteProperty", 50 | "InheritanceType": "None", 51 | "ObjectType": "Description", 52 | "InheritedObjectType": "", 53 | "AccessControlType": "Allow", 54 | "Identity": "%DomainName%\\Domain Admins" 55 | }, 56 | { 57 | "ObjectCategory": "DomainController", 58 | "ActiveDirectoryRights": "WriteProperty", 59 | "InheritanceType": "None", 60 | "ObjectType": "User-Logon", 61 | "InheritedObjectType": "", 62 | "AccessControlType": "Allow", 63 | "Identity": "%DomainName%\\Domain Admins" 64 | }, 65 | { 66 | "ObjectCategory": "msDFSR-LocalSettings", 67 | "ActiveDirectoryRights": "-1", 68 | "InheritanceType": "Descendents", 69 | "ObjectType": "", 70 | "InheritedObjectType": "", 71 | "AccessControlType": "Allow", 72 | "Identity": "" 73 | }, 74 | { 75 | "ObjectCategory": "msDFSR-LocalSettings", 76 | "ActiveDirectoryRights": "GenericAll", 77 | "InheritanceType": "None", 78 | "ObjectType": "", 79 | "InheritedObjectType": "", 80 | "AccessControlType": "Allow", 81 | "Identity": "" 82 | } 83 | ] -------------------------------------------------------------------------------- /ADMF/xml/ADMF.Format.ps1xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | ADMF.Context 7 | 8 | ADMF.Context 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | Name 24 | 25 | 26 | Version 27 | 28 | 29 | Weight 30 | 31 | 32 | Store 33 | 34 | 35 | Description 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | ADMF.Context.Store 46 | 47 | ADMF.Context.Store 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | Name 61 | 62 | 63 | PathExists 64 | 65 | 66 | Path 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /ADMF/internal/data/context/domain/readme.md: -------------------------------------------------------------------------------- 1 | # Domain Configuration 2 | 3 | ## General Content 4 | 5 | To define domain level objects and settings, place correctly formatted json files in their respective subfolders. 6 | See each folder's readme file for more info on how to define these objects. 7 | 8 | ## Domain Content Mode 9 | 10 | The content mode of a domain governs, how aggressively the DomainManagement modules goes about objects, not defined in the configuration. 11 | For example, if there were a user object not defined in configuration, do we delete that user? 12 | 13 | This is where we define, whether we say "Consider everything, except for the following OUs" or "Consider only the following OUs". 14 | 15 | ### Mode 16 | 17 | The mode defines, whether we consider anything at all beyond what we define: 18 | 19 | + *Additive:* Do not consider anything outside of configuration. 20 | + *Constrained:* Consider content in OUs defined by the Include & Exclude rules 21 | 22 | If multiple contexts define this property, the last one wins. 23 | 24 | ### Include/Exclude 25 | 26 | Using the Include & Exclude string arrays, we can then define a list of OUs to "take under management" or "exclude from management". 27 | That is, define OU structures, which are subject to having undefined objects deleted from, or OUs which are exempt from that. 28 | 29 | > OUs are defined by their distinguished name. Note that %DomainDN% is automatically available as a name label. 30 | 31 | Settings from multiple contexts will be merged. 32 | If only Exclude rules are defined, the system implicitly creates an Include rule for "%DomainDN%". 33 | If a later context then adds an Include rule, this no longer happens, possibly failing to apply your intent. 34 | If you wish to mix both - or know there is a risk of it happening - and still want everything to be under management by default, manually define "%DomainDN%" as include rule. 35 | 36 | ### UserExcludePattern 37 | 38 | Some users are hard to pre-define, and thus need to be excluded from prevention without being able to pre-determine their names explicitly. 39 | Very common case: Exchange System Mailbox users. 40 | For those you can define name patterns (using regex) to spare them from deletion. 41 | 42 | ### Defining Content Mode 43 | 44 | To define the Content Mode, place a json file named "content_mode.json" in the domain folder of the context (right beside this file). 45 | Unused properties are optional. 46 | 47 | Example content: 48 | 49 | ```json 50 | { 51 | "Mode": "Constrained", 52 | "Include": [], 53 | "Exclude": [ 54 | "OU=Resources,%DomainDN%", 55 | "OU=Clients,%DomainDN%", 56 | "OU=Company,%DomainDN%" 57 | ], 58 | "UserExcludePattern": [] 59 | } 60 | ``` 61 | 62 | ## Domain Level 63 | 64 | You can define the functional level of the domain in configuration. 65 | To do so, place a file named `domain_level.json` straight in the domain folder. 66 | 67 | This file should be a simple json object with a single property: Level. 68 | 69 | Example content: 70 | 71 | ```json 72 | { 73 | "Level": "2016" 74 | } 75 | ``` 76 | 77 | Supported values: 2008R2, 2012, 2012R2 or 2016 78 | 79 | > It is impossible to downgrade a domain functional level, so even if you define a low level, it will not be applied if the domain is already at a higher level. -------------------------------------------------------------------------------- /ADMF/internal/data/context/domain/gpregistrysettings/readme.md: -------------------------------------------------------------------------------- 1 | # Group Policy Registry Setting 2 | 3 | ## Synopsis 4 | 5 | The Group Policy Component allows defining Group Policy objects. 6 | However, it can only take a GPO backup and apply it. 7 | 8 | At this time, it is not possible to fully define a GPO using this solution. 9 | However, with this GP Registry Setting Component, we can now enable you to define custom registry settings (as if applied using Set-GPRegistryValue, which is indeed the cmdlet used behind the scenes). 10 | 11 | ## Description 12 | 13 | > Applying these settings independent of the Group Policy Component would conflict with its change tracking capabilities. Thus the two are processed in a single step, and _it is impossible to define registry settings for grooup policy objects not defined in configuration_ ! 14 | 15 | Most properties/parameters defining these registry settings support the Name and the Domain Data Components, allowing you to dynamically insert values into most stages of the process. 16 | 17 | To define any settings, place any arbitrary number of json files in this folder, each containing one or more registry setting definitions. 18 | 19 | ## Examples 20 | 21 | There are two basic scenarios: 22 | 23 | + Assign a straight value 24 | + Assign a value received from Domain Data (without any string coercion of name replacements) 25 | 26 | > Example 1 27 | 28 | ```json 29 | { 30 | "PolicyName": "C-S-CA-EnrollmentPolicy", 31 | "Key": "HKEY_LOCAL_MACHINE\\SOFTWARE\\Policies\\Microsoft\\Cryptography\\PolicyServers\\37c9dc30f207f27f61a2f7c3aed598a6e2920b54", 32 | "ValueName": "AuthFlags", 33 | "Type": "DWord", 34 | "Value": 2 35 | } 36 | ``` 37 | 38 | In this example, it will take the straight value of 2 and apply it to the specified key/name. 39 | 40 | > Example 2 41 | 42 | ```json 43 | { 44 | "PolicyName": "C-S-CA-EnrollmentPolicy", 45 | "Key": "HKEY_LOCAL_MACHINE\\SOFTWARE\\Policies\\Microsoft\\Cryptography\\PolicyServers", 46 | "ValueName": "(Default)", 47 | "Type": "String", 48 | "DomainData": "PolicyID" 49 | } 50 | ``` 51 | 52 | In this example, it will set the default value of the specified key to the data that was returned by the Domain Data Component's "PolicyID" script. 53 | For more details on defining Domain Data Component resources, see the relevant readme or website. 54 | 55 | ## Properties 56 | 57 | ### PolicyName 58 | 59 | The name of the group policy to apply the setting to. 60 | 61 | > Subject to advanced string insertion. 62 | 63 | ### Key 64 | 65 | The registry key, which to create if needed to appley the name/value pair. 66 | 67 | > Subject to advanced string insertion. 68 | 69 | ### ValueName 70 | 71 | The name of the value to define in registry. 72 | 73 | > Subject to advanced string insertion. 74 | 75 | ### Type 76 | 77 | The type of data written to registry. 78 | 79 | Legal values: 'Binary', 'DWord', 'ExpandString', 'MultiString', 'QWord', 'String' 80 | 81 | ### Value 82 | 83 | The straight value to write to registry. 84 | 85 | > Mutually exclusive with DomainData 86 | 87 | ### DomainData 88 | 89 | Rather than providing a straight value, specify the name of a script defined in the Domain Data component. 90 | The return value of that script will be written to registry. 91 | 92 | > Mutually exclusive with Value 93 | -------------------------------------------------------------------------------- /ADMF/ADMF.psm1: -------------------------------------------------------------------------------- 1 | $script:ModuleRoot = $PSScriptRoot 2 | $script:ModuleVersion = (Import-PowerShellDataFile -Path "$($script:ModuleRoot)\ADMF.psd1").ModuleVersion 3 | 4 | # Detect whether at some level dotsourcing was enforced 5 | $script:doDotSource = Get-PSFConfigValue -FullName ADMF.Import.DoDotSource -Fallback $false 6 | if ($ADMF_dotsourcemodule) { $script:doDotSource = $true } 7 | 8 | <# 9 | Note on Resolve-Path: 10 | All paths are sent through Resolve-Path/Resolve-PSFPath in order to convert them to the correct path separator. 11 | This allows ignoring path separators throughout the import sequence, which could otherwise cause trouble depending on OS. 12 | Resolve-Path can only be used for paths that already exist, Resolve-PSFPath can accept that the last leaf my not exist. 13 | This is important when testing for paths. 14 | #> 15 | 16 | # Detect whether at some level loading individual module files, rather than the compiled module was enforced 17 | $importIndividualFiles = Get-PSFConfigValue -FullName ADMF.Import.IndividualFiles -Fallback $false 18 | if ($ADMF_importIndividualFiles) { $importIndividualFiles = $true } 19 | if (Test-Path (Resolve-PSFPath -Path "$($script:ModuleRoot)\..\.git" -SingleItem -NewChild)) { $importIndividualFiles = $true } 20 | if ("" -eq '') { $importIndividualFiles = $true } 21 | 22 | function Import-ModuleFile 23 | { 24 | <# 25 | .SYNOPSIS 26 | Loads files into the module on module import. 27 | 28 | .DESCRIPTION 29 | This helper function is used during module initialization. 30 | It should always be dotsourced itself, in order to proper function. 31 | 32 | This provides a central location to react to files being imported, if later desired 33 | 34 | .PARAMETER Path 35 | The path to the file to load 36 | 37 | .EXAMPLE 38 | PS C:\> . Import-ModuleFile -File $function.FullName 39 | 40 | Imports the file stored in $function according to import policy 41 | #> 42 | [CmdletBinding()] 43 | Param ( 44 | [string] 45 | $Path 46 | ) 47 | 48 | $resolvedPath = $ExecutionContext.SessionState.Path.GetResolvedPSPathFromPSPath($Path).ProviderPath 49 | if ($doDotSource) { . $resolvedPath } 50 | else { $ExecutionContext.InvokeCommand.InvokeScript($false, ([scriptblock]::Create([io.file]::ReadAllText($resolvedPath))), $null, $null) } 51 | } 52 | 53 | #region Load individual files 54 | if ($importIndividualFiles) 55 | { 56 | # Execute Preimport actions 57 | . Import-ModuleFile -Path "$ModuleRoot\internal\scripts\preimport.ps1" 58 | 59 | # Import all internal functions 60 | foreach ($function in (Get-ChildItem "$ModuleRoot\internal\functions" -Filter "*.ps1" -Recurse -ErrorAction Ignore)) 61 | { 62 | . Import-ModuleFile -Path $function.FullName 63 | } 64 | 65 | # Import all public functions 66 | foreach ($function in (Get-ChildItem "$ModuleRoot\functions" -Filter "*.ps1" -Recurse -ErrorAction Ignore)) 67 | { 68 | . Import-ModuleFile -Path $function.FullName 69 | } 70 | 71 | # Execute Postimport actions 72 | . Import-ModuleFile -Path "$ModuleRoot\internal\scripts\postimport.ps1" 73 | 74 | # End it here, do not load compiled code below 75 | return 76 | } 77 | #endregion Load individual files 78 | 79 | #region Load compiled code 80 | "" 81 | #endregion Load compiled code -------------------------------------------------------------------------------- /ADMF/internal/data/context/domain/accessrulemodes/readme.md: -------------------------------------------------------------------------------- 1 | # AccessRule Processing Mode 2 | 3 | ## Description 4 | 5 | When processing AccessRules, by default all undesired rules on a configured object will be removed. 6 | This is great for objects fully under your control. 7 | But what about scenarios where you just want to enable another process to self-manage an environment? 8 | At that point, where other servies may interfere with your deployed configuration you may want to instead define your settings as a minimum while disregarding/ignoring other rules defined. 9 | 10 | This is what the AccessRule Processing Mode controls. 11 | 12 | With that you can define which objects to fully control and where to just add to. 13 | You can target by: 14 | 15 | + Explicit Path 16 | + SubTree under a specific path 17 | + ObjectCategory 18 | 19 | ## Precedence 20 | 21 | With the various targeting modes, several rules may apply to the same AD object. 22 | When that happens, the settings are applied in the following, descending precedence: 23 | 24 | + Explicit Path 25 | + ObjectCategory 26 | + SubTree-item with the longest name among applicable SubTree-definitions 27 | 28 | This allows to define multiple SubTree tiers, where only the most specific one will apply to a given object. 29 | 30 | ## Overriding settings 31 | 32 | Each Processing Mode can be overridden by a different Context. 33 | A Processing Mode is identified by: PathMode & Path or ObjectCategory. 34 | 35 | This means an Explicit Path rule can only be overriden by a matching Explicit Path rule (which may have a different Processing Mode). 36 | 37 | ## Example Configurations 38 | 39 | Configuration entries can be defined by adding them to the current folder as json files. 40 | The specific filename is not important and any number of configuration files can be defined. 41 | 42 | > Explicit Path Rule 43 | 44 | ```json 45 | { 46 | "Path": "CN=MyGroup3,OU=Groups,OU=DomainContent,%DomainDN%", 47 | "Mode": "Additive" 48 | } 49 | ``` 50 | 51 | > SubTree Path Rule 52 | 53 | ```json 54 | { 55 | "Path": "OU=Groups,OU=DomainContent,%DomainDN%", 56 | "PathMode": "SubTree", 57 | "Mode": "Additive" 58 | } 59 | ``` 60 | 61 | > ObjectCategory Rule 62 | 63 | ```json 64 | { 65 | "ObjectCategory": "DomainControllers", 66 | "Mode": "Constrained" 67 | } 68 | ``` 69 | 70 | ## Parameters 71 | 72 | ### Path 73 | 74 | > Supports string replacement 75 | 76 | The path in ad to the targeted item. 77 | Whether this is an Explicit or SubTree Path-rule is determined by the `PathMode` rule. 78 | 79 | ### PathMode 80 | 81 | Optional: true | Options: SingleItem, SubTree | Default: SingleItem 82 | 83 | Determines whether a Path-rule is either Explicit or SubTree: 84 | 85 | + SingleItem: Processing Mode only applies to the exactly specified path. 86 | + SubTree: Processing Mode applies to specified path and all items beneath it (unless overridden). 87 | 88 | ### ObjectCategory 89 | 90 | Instead of targeting by path, target a specific ObjectCategory. 91 | 92 | ### Mode 93 | 94 | Options: 'Constrained', 'Additive' 95 | 96 | The AccessRule Processing Mode to apply: 97 | 98 | + Constrained: Any non-defined AccessRule will be removed 99 | + Additive: Any non-defined AccessRule will be ignored 100 | + Defined: Any non-defined [AccessRule](accessrules.html) will be ignored, _unless_ it affects an identity for which a non-matching definition exists. 101 | -------------------------------------------------------------------------------- /ADMF/tests/general/FileIntegrity.Tests.ps1: -------------------------------------------------------------------------------- 1 | $moduleRoot = (Resolve-Path "$global:testroot\..").Path 2 | 3 | . "$global:testroot\general\FileIntegrity.Exceptions.ps1" 4 | 5 | Describe "Verifying integrity of module files" { 6 | BeforeAll { 7 | function Get-FileEncoding 8 | { 9 | <# 10 | .SYNOPSIS 11 | Tests a file for encoding. 12 | 13 | .DESCRIPTION 14 | Tests a file for encoding. 15 | 16 | .PARAMETER Path 17 | The file to test 18 | #> 19 | [CmdletBinding()] 20 | Param ( 21 | [Parameter(Mandatory = $True, ValueFromPipelineByPropertyName = $True)] 22 | [Alias('FullName')] 23 | [string] 24 | $Path 25 | ) 26 | 27 | if ($PSVersionTable.PSVersion.Major -lt 6) 28 | { 29 | [byte[]]$byte = get-content -Encoding byte -ReadCount 4 -TotalCount 4 -Path $Path 30 | } 31 | else 32 | { 33 | [byte[]]$byte = Get-Content -AsByteStream -ReadCount 4 -TotalCount 4 -Path $Path 34 | } 35 | 36 | if ($byte[0] -eq 0xef -and $byte[1] -eq 0xbb -and $byte[2] -eq 0xbf) { 'UTF8 BOM' } 37 | elseif ($byte[0] -eq 0xfe -and $byte[1] -eq 0xff) { 'Unicode' } 38 | elseif ($byte[0] -eq 0 -and $byte[1] -eq 0 -and $byte[2] -eq 0xfe -and $byte[3] -eq 0xff) { 'UTF32' } 39 | elseif ($byte[0] -eq 0x2b -and $byte[1] -eq 0x2f -and $byte[2] -eq 0x76) { 'UTF7' } 40 | else { 'Unknown' } 41 | } 42 | } 43 | 44 | Context "Validating PS1 Script files" { 45 | $allFiles = Get-ChildItem -Path $moduleRoot -Recurse | Where-Object Name -like "*.ps1" | Where-Object FullName -NotLike "$moduleRoot\tests\*" 46 | 47 | foreach ($file in $allFiles) 48 | { 49 | $name = $file.FullName.Replace("$moduleRoot\", '') 50 | 51 | It "[$name] Should have UTF8 encoding with Byte Order Mark" -TestCases @{ file = $file } { 52 | Get-FileEncoding -Path $file.FullName | Should -Be 'UTF8 BOM' 53 | } 54 | 55 | It "[$name] Should have no trailing space" -TestCases @{ file = $file } { 56 | ($file | Select-String "\s$" | Where-Object { $_.Line.Trim().Length -gt 0}).LineNumber | Should -BeNullOrEmpty 57 | } 58 | 59 | $tokens = $null 60 | $parseErrors = $null 61 | $ast = [System.Management.Automation.Language.Parser]::ParseFile($file.FullName, [ref]$tokens, [ref]$parseErrors) 62 | 63 | It "[$name] Should have no syntax errors" -TestCases @{ parseErrors = $parseErrors } { 64 | $parseErrors | Should -BeNullOrEmpty 65 | } 66 | 67 | foreach ($command in $global:BannedCommands) 68 | { 69 | if ($global:MayContainCommand["$command"] -notcontains $file.Name) 70 | { 71 | It "[$name] Should not use $command" -TestCases @{ tokens = $tokens; command = $command } { 72 | $tokens | Where-Object Text -EQ $command | Should -BeNullOrEmpty 73 | } 74 | } 75 | } 76 | } 77 | } 78 | 79 | Context "Validating help.txt help files" { 80 | $allFiles = Get-ChildItem -Path $moduleRoot -Recurse | Where-Object Name -like "*.help.txt" | Where-Object FullName -NotLike "$moduleRoot\tests\*" 81 | 82 | foreach ($file in $allFiles) 83 | { 84 | $name = $file.FullName.Replace("$moduleRoot\", '') 85 | 86 | It "[$name] Should have UTF8 encoding" -TestCases @{ file = $file } { 87 | Get-FileEncoding -Path $file.FullName | Should -Be 'UTF8 BOM' 88 | } 89 | 90 | It "[$name] Should have no trailing space" -TestCases @{ file = $file } { 91 | ($file | Select-String "\s$" | Where-Object { $_.Line.Trim().Length -gt 0 } | Measure-Object).Count | Should -Be 0 92 | } 93 | } 94 | } 95 | } -------------------------------------------------------------------------------- /ADMF/internal/configurations/configuration.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | This is an example configuration file 3 | 4 | By default, it is enough to have a single one of them, 5 | however if you have enough configuration settings to justify having multiple copies of it, 6 | feel totally free to split them into multiple files. 7 | #> 8 | 9 | <# 10 | # Example Configuration 11 | Set-PSFConfig -Module 'ADMF' -Name 'Example.Setting' -Value 10 -Initialize -Validation 'integer' -Handler { } -Description "Example configuration setting. Your module can then use the setting using 'Get-PSFConfigValue'" 12 | #> 13 | 14 | Set-PSFConfig -Module 'ADMF' -Name 'Import.DoDotSource' -Value $false -Initialize -Validation 'bool' -Description "Whether the module files should be dotsourced on import. By default, the files of this module are read as string value and invoked, which is faster but worse on debugging." 15 | Set-PSFConfig -Module 'ADMF' -Name 'Import.IndividualFiles' -Value $false -Initialize -Validation 'bool' -Description "Whether the module files should be imported individually. During the module build, all module code is compiled into few files, which are imported instead by default. Loading the compiled versions is faster, using the individual files is easier for debugging and testing out adjustments." 16 | 17 | Set-PSFConfig -Module 'ADMF' -Name 'DCSelectionMode' -Value 'PDCEmulator' -Initialize -Validation 'DCSelectionMode' -Description 'When executing commands, specifying the domain name will cause the module to resolve to a single DC to work against. This setting governs the algorythm that determines the DC to work against. Either "PDCEmulator", "Site" or "Random" are valid choices. When using site, specify the "ADMF.DCSelection.Site" setting as well to select the site to prefer.' 18 | Set-PSFConfig -Module 'ADMF' -Name 'DCSelection.Site' -Value '' -Initialize -Validation 'stringarray' -Description 'When using the "ADMF.DCSelectionMode" in "Site" mode, specifying this setting will pick the site to chose. If there are multiple DCs in the target site, the PDCEmulator will be preferred if present.' 19 | Set-PSFConfig -Module 'ADMF' -Name 'DCSelection.Site.Prioritize' -Value $true -Initialize -Validation bool -Description 'When using the "ADMF.DCSelectionMode" in "Site" mode, this setting governs whether all sites are pooled ($false) or whether processed one after the other until a valid DC has been found ($true).' 20 | 21 | Set-PSFConfig -Module 'ADMF' -Name 'VerboseExecution' -Value $true -Initialize -Validation bool -Handler { 22 | if ($args[0]) 23 | { 24 | $null = New-PSFMessageLevelModifier -Name ADMF_Verbose -Modifier 0 -IncludeModuleName ADMF 25 | } 26 | else 27 | { 28 | $null = New-PSFMessageLevelModifier -Name ADMF_Verbose -Modifier 3 -IncludeModuleName ADMF 29 | } 30 | } -Description 'Enabling this will cause the ADMF module to be more verbose by default' 31 | 32 | Set-PSFConfig -Module 'ADMF' -Name 'Context.Store.Default' -Value "$(Get-PSFPath -Name AppData)\ADMF\Contexts" -Initialize -Validation string -Description 'The default path in which ADMF will look for configuration contexts. Add additional such paths by declaring additional settings labeled "ADMF.Context.Store.*"' 33 | Set-PSFConfig -Module 'ADMF' -Name 'DCInstall.Context.Prompt.Enable' -Value $true -Initialize -Validation 'bool' -Description "Whether the DC installation commands should generate Context selection prompts." 34 | 35 | Set-PSFConfig -Module 'ADMF' -Name 'PowerShellGet.UseV3' -Value $false -Initialize -Validation bool -Description 'Whether to use PowerShellGet V3 by default or not.' -------------------------------------------------------------------------------- /ADMF/internal/data/context/forest/schemaDefaultPermissions/readme.md: -------------------------------------------------------------------------------- 1 | # Schema Default Permissions 2 | 3 | ## Description 4 | 5 | The Schema defines how objects in attributes look like. 6 | What attributes do they have? 7 | How do they replicate? 8 | Who is allowed to do what? 9 | 10 | Especially of interest from a security perspective: 11 | For object classes, it defines the default permissions applied to a new object of that class. 12 | For example, it defines the permission on any new group policy object that is created. 13 | 14 | > The DomainManagement Component `AccessRules` uses these default permissions to avoid forcing you to define every single access rule. 15 | 16 | An attacker that modifies the default permissions could gain persistence and trick scanners that automatically exclude default permissions from their analysis. 17 | 18 | This Component allows you to define those default permissions, ensuring no unexpected defaults were applied and easily manage intended customizations. 19 | 20 | ## Default Forest Settings 21 | 22 | The default settings in a new forest are already quite massive. 23 | If you want to fully define all permissions, you would need to define every single one of those. 24 | 25 | As this would be quite tedious, we have included those in the ADMF-builtin Context template, if you generate it with the `-DefaultAccessRules` parameter. 26 | E.g.: 27 | 28 | ```powershell 29 | New-AdmfContext -Name ContosoDefault -DefaultAccessRules 30 | ``` 31 | 32 | Custom object classes are not taken into consideration and you need to build that configuration yourself. 33 | 34 | ## Example Configuration Setting 35 | 36 | This setting will add the group "S-GpoAdmins" of the forest root domain to the default permissions for all new group policy objects with full control permissions: 37 | 38 | ```json 39 | [ 40 | { 41 | "ClassName": "Group-Policy-Container", 42 | "Identity": "%RootDomainName%\\S-GpoAdmins", 43 | "ActiveDirectoryRights": "CreateChild, DeleteChild, Self, WriteProperty, DeleteTree, Delete, GenericRead, WriteDacl, WriteOwner", 44 | "AccessControlType": "Allow", 45 | "InheritanceType": "All", 46 | "ObjectType": "00000000-0000-0000-0000-000000000000", 47 | "InheritedObjectType": "00000000-0000-0000-0000-000000000000", 48 | "Mode": "Constrained" 49 | } 50 | ] 51 | ``` 52 | 53 | ## Parameters 54 | 55 | ### ClassName 56 | 57 | The name of the object class in schema this applies to. 58 | 59 | ### Identity 60 | 61 | The principal to which the access rule applies. 62 | Supports limited string resolution. 63 | 64 | ### ActiveDirectoryRights 65 | 66 | The rights granted. 67 | 68 | ### AccessControlType 69 | 70 | Allow or Deny? 71 | Defaults to: Allow 72 | 73 | ### InheritanceType 74 | 75 | How is this privilege inherited by child objects? 76 | 77 | ### ObjectType 78 | 79 | What object types does this permission apply to? 80 | 81 | ### InheritedObjectType 82 | 83 | What object types does this permission apply to? 84 | Used for extended properties. 85 | 86 | ### Mode 87 | 88 | How access rules are actually applied: 89 | 90 | - Additive: Only add new access rules, but do not touch existing ones 91 | - Defined: Add new access rules, remove access rules not defined in configuration that apply to a principal that has access rules defined. 92 | - Constrained: Add new access rules, remove all access rules not defined in configuration 93 | 94 | All Modes of all settings for a given class are used when determining the effective Mode applied to that class. 95 | The most restrictive Mode applies. 96 | -------------------------------------------------------------------------------- /ADMF/functions/Test-AdmfDC.ps1: -------------------------------------------------------------------------------- 1 | function Test-AdmfDC 2 | { 3 | <# 4 | .SYNOPSIS 5 | Tests whether all DCs in the target domain are in the desired state. 6 | 7 | .DESCRIPTION 8 | Tests whether all DCs in the target domain are in the desired state. 9 | 10 | .PARAMETER Server 11 | The server / domain to work with. 12 | 13 | .PARAMETER Credential 14 | The credentials to use for this operation. 15 | 16 | .PARAMETER TargetServer 17 | The specific server(s) to process. 18 | If specified, only listed domain controllers will be affected. 19 | Specify the full FQDN of the server. 20 | 21 | .PARAMETER Options 22 | What tests to execute. 23 | Defaults to all tests. 24 | 25 | .PARAMETER CredentialProvider 26 | The credential provider to use to resolve the input credentials. 27 | See help on Register-AdmfCredentialProvider for details. 28 | 29 | .PARAMETER ContextPrompt 30 | Force displaying the Context selection User Interface. 31 | 32 | .EXAMPLE 33 | PS C:\> Test-AdmfDC 34 | 35 | Tests the current domain's DCs whether they are compliant with the desired/defined state 36 | #> 37 | [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingPlainTextForPassword', '')] 38 | [CmdletBinding()] 39 | param ( 40 | [PSFComputer] 41 | $Server, 42 | 43 | [PSCredential] 44 | $Credential, 45 | 46 | [string[]] 47 | $TargetServer = @(), 48 | 49 | [UpdateDCOptions[]] 50 | $Options = 'All', 51 | 52 | [string] 53 | $CredentialProvider = 'default', 54 | 55 | [Alias('Ctx')] 56 | [switch] 57 | $ContextPrompt 58 | ) 59 | 60 | begin 61 | { 62 | Reset-DomainControllerCache 63 | $parameters = $PSBoundParameters | ConvertTo-PSFHashtable -Include Server, Credential 64 | if (-not $Server -and $TargetServer) { 65 | $parameters.Server = $TargetServer | Select-Object -First 1 66 | } 67 | $originalArgument = Invoke-PreCredentialProvider @parameters -ProviderName $CredentialProvider -Parameter $parameters -Cmdlet $PSCmdlet 68 | try { $parameters.Server = Resolve-DomainController @parameters -ErrorAction Stop -Confirm:$false } 69 | catch 70 | { 71 | Invoke-PostCredentialProvider -ProviderName $CredentialProvider -Server $originalArgument.Server -Credential $originalArgument.Credential -Cmdlet $PSCmdlet 72 | throw 73 | } 74 | Invoke-PSFCallback -Data $parameters -EnableException $true -PSCmdlet $PSCmdlet 75 | Set-AdmfContext @parameters -Interactive -ReUse:$(-not $ContextPrompt) -EnableException 76 | [UpdateDCOptions]$newOptions = $Options 77 | } 78 | process 79 | { 80 | try 81 | { 82 | if ($newOptions -band [UpdateDCOptions]::Share) 83 | { 84 | if (Get-DCShare) 85 | { 86 | Write-PSFMessage -Level Host -String 'Test-AdmfDC.Executing.Test' -StringValues 'Shares', $parameters.Server 87 | Test-DCShare @parameters -TargetServer $TargetServer 88 | } 89 | else { Write-PSFMessage -Level Host -String 'Test-AdmfDC.Skipping.Test.NoConfiguration' -StringValues 'Shares' } 90 | } 91 | if ($newOptions -band [UpdateDCOptions]::FSAccessRule) 92 | { 93 | if (Get-DCAccessRule) 94 | { 95 | Write-PSFMessage -Level Host -String 'Test-AdmfDC.Executing.Test' -StringValues 'FSAccessRules', $parameters.Server 96 | Test-DCAccessRule @parameters -TargetServer $TargetServer 97 | } 98 | else { Write-PSFMessage -Level Host -String 'Test-AdmfDC.Skipping.Test.NoConfiguration' -StringValues 'FSAccessRules' } 99 | } 100 | } 101 | catch { throw } 102 | finally { 103 | Disable-PSFConsoleInterrupt 104 | try { Invoke-PostCredentialProvider -ProviderName $CredentialProvider -Server $originalArgument.Server -Credential $originalArgument.Credential -Cmdlet $PSCmdlet } 105 | finally { Enable-PSFConsoleInterrupt } 106 | } 107 | } 108 | } -------------------------------------------------------------------------------- /ADMF/internal/data/domainDefaults/accessRules/addefault_cn_Keys.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "Path": "CN=Keys,%DomainDN%", 4 | "ActiveDirectoryRights": "GenericAll", 5 | "InheritanceType": "All", 6 | "ObjectType": "", 7 | "InheritedObjectType": "", 8 | "AccessControlType": "Allow", 9 | "Identity": "NT AUTHORITY\\ENTERPRISE DOMAIN CONTROLLERS", 10 | "Optional": true 11 | }, 12 | { 13 | "Path": "CN=Keys,%DomainDN%", 14 | "ActiveDirectoryRights": "GenericAll", 15 | "InheritanceType": "All", 16 | "ObjectType": "", 17 | "InheritedObjectType": "", 18 | "AccessControlType": "Allow", 19 | "Identity": "NT AUTHORITY\\SYSTEM", 20 | "Optional": true 21 | }, 22 | { 23 | "Path": "CN=Keys,%DomainDN%", 24 | "ActiveDirectoryRights": "GenericAll", 25 | "InheritanceType": "none", 26 | "ObjectType": "", 27 | "InheritedObjectType": "", 28 | "AccessControlType": "Allow", 29 | "Identity": "NT AUTHORITY\\SYSTEM", 30 | "Optional": true, 31 | "Present": "false" 32 | }, 33 | { 34 | "Path": "CN=Keys,%DomainDN%", 35 | "ActiveDirectoryRights": "GenericAll", 36 | "InheritanceType": "All", 37 | "ObjectType": "", 38 | "InheritedObjectType": "", 39 | "AccessControlType": "Allow", 40 | "Identity": "%DomainSID%-512", 41 | "Optional": true 42 | }, 43 | { 44 | "Path": "CN=Keys,%DomainDN%", 45 | "ActiveDirectoryRights": "GenericAll", 46 | "InheritanceType": "None", 47 | "ObjectType": "", 48 | "InheritedObjectType": "", 49 | "AccessControlType": "Allow", 50 | "Identity": "%DomainSID%-512", 51 | "Optional": true, 52 | "Present": "false" 53 | }, 54 | { 55 | "Path": "CN=Keys,%DomainDN%", 56 | "ActiveDirectoryRights": "GenericAll", 57 | "InheritanceType": "All", 58 | "ObjectType": "", 59 | "InheritedObjectType": "", 60 | "AccessControlType": "Allow", 61 | "Identity": "%DomainName%\\Domain Controllers", 62 | "Optional": true 63 | }, 64 | { 65 | "Path": "CN=Keys,%DomainDN%", 66 | "ActiveDirectoryRights": "GenericAll", 67 | "InheritanceType": "All", 68 | "ObjectType": "", 69 | "InheritedObjectType": "", 70 | "AccessControlType": "Allow", 71 | "Identity": "%RootDomainName%\\Enterprise Admins", 72 | "Optional": true 73 | }, 74 | { 75 | "Path": "CN=Keys,%DomainDN%", 76 | "ActiveDirectoryRights": "GenericAll", 77 | "InheritanceType": "All", 78 | "ObjectType": "", 79 | "InheritedObjectType": "", 80 | "AccessControlType": "Allow", 81 | "Identity": "%DomainName%\\Key Admins", 82 | "Optional": true 83 | }, 84 | { 85 | "Path": "CN=Keys,%DomainDN%", 86 | "ActiveDirectoryRights": "GenericAll", 87 | "InheritanceType": "All", 88 | "ObjectType": "", 89 | "InheritedObjectType": "", 90 | "AccessControlType": "Allow", 91 | "Identity": "%RootDomainName%\\Enterprise Key Admins", 92 | "Optional": true 93 | }, 94 | { 95 | "Path": "CN=Keys,%DomainDN%", 96 | "ActiveDirectoryRights": "GenericRead", 97 | "InheritanceType": "None", 98 | "ObjectType": "", 99 | "InheritedObjectType": "", 100 | "AccessControlType": "Allow", 101 | "Identity": "S-1-5-11", 102 | "Optional": true, 103 | "Present": "false" 104 | } 105 | ] -------------------------------------------------------------------------------- /ADMF/internal/components/DefaultAccessRules/domain/accessRules/addefault_cn_Keys.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "Path": "CN=Keys,%DomainDN%", 4 | "ActiveDirectoryRights": "GenericAll", 5 | "InheritanceType": "All", 6 | "ObjectType": "", 7 | "InheritedObjectType": "", 8 | "AccessControlType": "Allow", 9 | "Identity": "NT AUTHORITY\\ENTERPRISE DOMAIN CONTROLLERS", 10 | "Optional": true 11 | }, 12 | { 13 | "Path": "CN=Keys,%DomainDN%", 14 | "ActiveDirectoryRights": "GenericAll", 15 | "InheritanceType": "All", 16 | "ObjectType": "", 17 | "InheritedObjectType": "", 18 | "AccessControlType": "Allow", 19 | "Identity": "NT AUTHORITY\\SYSTEM", 20 | "Optional": true 21 | }, 22 | { 23 | "Path": "CN=Keys,%DomainDN%", 24 | "ActiveDirectoryRights": "GenericAll", 25 | "InheritanceType": "none", 26 | "ObjectType": "", 27 | "InheritedObjectType": "", 28 | "AccessControlType": "Allow", 29 | "Identity": "NT AUTHORITY\\SYSTEM", 30 | "Optional": true, 31 | "Present": "false" 32 | }, 33 | { 34 | "Path": "CN=Keys,%DomainDN%", 35 | "ActiveDirectoryRights": "GenericAll", 36 | "InheritanceType": "All", 37 | "ObjectType": "", 38 | "InheritedObjectType": "", 39 | "AccessControlType": "Allow", 40 | "Identity": "%DomainSID%-512", 41 | "Optional": true 42 | }, 43 | { 44 | "Path": "CN=Keys,%DomainDN%", 45 | "ActiveDirectoryRights": "GenericAll", 46 | "InheritanceType": "None", 47 | "ObjectType": "", 48 | "InheritedObjectType": "", 49 | "AccessControlType": "Allow", 50 | "Identity": "%DomainSID%-512", 51 | "Optional": true, 52 | "Present": "false" 53 | }, 54 | { 55 | "Path": "CN=Keys,%DomainDN%", 56 | "ActiveDirectoryRights": "GenericAll", 57 | "InheritanceType": "All", 58 | "ObjectType": "", 59 | "InheritedObjectType": "", 60 | "AccessControlType": "Allow", 61 | "Identity": "%DomainName%\\Domain Controllers", 62 | "Optional": true 63 | }, 64 | { 65 | "Path": "CN=Keys,%DomainDN%", 66 | "ActiveDirectoryRights": "GenericAll", 67 | "InheritanceType": "All", 68 | "ObjectType": "", 69 | "InheritedObjectType": "", 70 | "AccessControlType": "Allow", 71 | "Identity": "%RootDomainName%\\Enterprise Admins", 72 | "Optional": true 73 | }, 74 | { 75 | "Path": "CN=Keys,%DomainDN%", 76 | "ActiveDirectoryRights": "GenericAll", 77 | "InheritanceType": "All", 78 | "ObjectType": "", 79 | "InheritedObjectType": "", 80 | "AccessControlType": "Allow", 81 | "Identity": "%DomainName%\\Key Admins", 82 | "Optional": true 83 | }, 84 | { 85 | "Path": "CN=Keys,%DomainDN%", 86 | "ActiveDirectoryRights": "GenericAll", 87 | "InheritanceType": "All", 88 | "ObjectType": "", 89 | "InheritedObjectType": "", 90 | "AccessControlType": "Allow", 91 | "Identity": "%RootDomainName%\\Enterprise Key Admins", 92 | "Optional": true 93 | }, 94 | { 95 | "Path": "CN=Keys,%DomainDN%", 96 | "ActiveDirectoryRights": "GenericRead", 97 | "InheritanceType": "None", 98 | "ObjectType": "", 99 | "InheritedObjectType": "", 100 | "AccessControlType": "Allow", 101 | "Identity": "S-1-5-11", 102 | "Optional": true, 103 | "Present": "false" 104 | } 105 | ] -------------------------------------------------------------------------------- /ADMF/ADMF.psd1: -------------------------------------------------------------------------------- 1 | @{ 2 | # Script module or binary module file associated with this manifest 3 | RootModule = 'ADMF.psm1' 4 | 5 | # Version number of this module. 6 | ModuleVersion = '1.14.112' 7 | 8 | # ID used to uniquely identify this module 9 | GUID = '43f2a890-942f-4dd7-bad0-b774b44ea849' 10 | 11 | # Author of this module 12 | Author = 'Friedrich Weinmann' 13 | 14 | # Company or vendor of this module 15 | CompanyName = 'Microsoft' 16 | 17 | # Copyright statement for this module 18 | Copyright = 'Copyright (c) 2019 Friedrich Weinmann' 19 | 20 | # Description of the functionality provided by this module 21 | Description = 'Central Management Component of the Active Directory Management Framework' 22 | 23 | # Minimum version of the Windows PowerShell engine required by this module 24 | PowerShellVersion = '5.1' 25 | 26 | # Modules that must be imported into the global environment prior to importing 27 | # this module 28 | RequiredModules = @( 29 | @{ ModuleName = 'PSFramework'; ModuleVersion = '1.13.416' } 30 | @{ ModuleName = 'ADSec'; ModuleVersion = '1.0.1' } 31 | @{ ModuleName = 'string'; ModuleVersion = '1.2.13' } 32 | @{ ModuleName = 'ResolveString'; ModuleVersion = '1.0.0' } 33 | @{ ModuleName = 'Principal'; ModuleVersion = '1.0.0' } 34 | @{ ModuleName = 'ADMF.Core'; ModuleVersion = '1.2.12' } 35 | @{ ModuleName = 'DCManagement'; ModuleVersion = '1.2.26' } 36 | @{ ModuleName = 'DomainManagement'; ModuleVersion = '1.9.234' } 37 | @{ ModuleName = 'ForestManagement'; ModuleVersion = '1.5.82' } 38 | ) 39 | 40 | # Assemblies that must be loaded prior to importing this module 41 | RequiredAssemblies = @( 42 | 'bin\ADMF.dll' 43 | 'System.Windows.Forms' 44 | ) 45 | 46 | # Type files (.ps1xml) to be loaded when importing this module 47 | # TypesToProcess = @('xml\ADMF.Types.ps1xml') 48 | 49 | # Format files (.ps1xml) to be loaded when importing this module 50 | FormatsToProcess = @('xml\ADMF.Format.ps1xml') 51 | 52 | # Functions to export from this module 53 | FunctionsToExport = @( 54 | 'Export-AdmfGpo' 55 | 'Get-AdmfContext' 56 | 'Get-AdmfContextStore' 57 | 'Invoke-AdmfDC' 58 | 'Invoke-AdmfDomain' 59 | 'Invoke-AdmfForest' 60 | 'Invoke-AdmfItem' 61 | 'New-AdmfContext' 62 | 'New-AdmfContextModule' 63 | 'New-AdmfContextStore' 64 | 'Publish-AdmfContext' 65 | 'Register-AdmfCredentialProvider' 66 | 'Set-AdmfContext' 67 | 'Test-AdmfDC' 68 | 'Test-AdmfDomain' 69 | 'Test-AdmfForest' 70 | ) 71 | 72 | # Cmdlets to export from this module 73 | # CmdletsToExport = '' 74 | 75 | # Variables to export from this module 76 | # VariablesToExport = '' 77 | 78 | # Aliases to export from this module 79 | AliasesToExport = @( 80 | 'iai' 81 | ) 82 | 83 | # List of all modules packaged with this module 84 | # ModuleList = @() 85 | 86 | # List of all files packaged with this module 87 | # FileList = @() 88 | 89 | # Private data to pass to the module specified in ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell. 90 | PrivateData = @{ 91 | 92 | #Support for PowerShellGet galleries. 93 | PSData = @{ 94 | 95 | # Tags applied to this module. These help with module discovery in online galleries. 96 | Tags = @('activedirectory','configuration','admf', 'management') 97 | 98 | # A URL to the license for this module. 99 | LicenseUri = 'https://github.com/ActiveDirectoryManagementFramework/ADMF/blob/master/LICENSE' 100 | 101 | # A URL to the main website for this project. 102 | ProjectUri = 'https://admf.one' 103 | 104 | # A URL to an icon representing this module. 105 | # IconUri = '' 106 | 107 | # ReleaseNotes of this module 108 | ReleaseNotes = 'https://github.com/ActiveDirectoryManagementFramework/ADMF/blob/master/ADMF/changelog.md' 109 | 110 | } # End of PSData hashtable 111 | 112 | } # End of PrivateData hashtable 113 | } -------------------------------------------------------------------------------- /ADMF/tests/pester.ps1: -------------------------------------------------------------------------------- 1 | param ( 2 | $TestGeneral = $true, 3 | 4 | $TestFunctions = $true, 5 | 6 | [ValidateSet('None', 'Normal', 'Detailed', 'Diagnostic')] 7 | [Alias('Show')] 8 | $Output = "None", 9 | 10 | $Include = "*", 11 | 12 | $Exclude = "" 13 | ) 14 | 15 | Write-PSFMessage -Level Important -Message "Starting Tests" 16 | 17 | Write-PSFMessage -Level Important -Message "Importing Module" 18 | 19 | $global:testroot = $PSScriptRoot 20 | $global:__pester_data = @{ } 21 | 22 | Remove-Module ADMF -ErrorAction Ignore 23 | Import-Module "$PSScriptRoot\..\ADMF.psd1" 24 | Import-Module "$PSScriptRoot\..\ADMF.psm1" -Force 25 | 26 | # Need to import explicitly so we can use the configuration class 27 | Import-Module Pester 28 | 29 | Write-PSFMessage -Level Important -Message "Creating test result folder" 30 | $null = New-Item -Path "$PSScriptRoot\..\.." -Name TestResults -ItemType Directory -Force 31 | 32 | $totalFailed = 0 33 | $totalRun = 0 34 | 35 | $testresults = @() 36 | $config = [PesterConfiguration]::Default 37 | $config.TestResult.Enabled = $true 38 | 39 | #region Run General Tests 40 | if ($TestGeneral) 41 | { 42 | Write-PSFMessage -Level Important -Message "Modules imported, proceeding with general tests" 43 | foreach ($file in (Get-ChildItem "$PSScriptRoot\general" | Where-Object Name -like "*.Tests.ps1")) 44 | { 45 | if ($file.Name -notlike $Include) { continue } 46 | if ($file.Name -like $Exclude) { continue } 47 | 48 | Write-PSFMessage -Level Significant -Message " Executing $($file.Name)" 49 | $config.TestResult.OutputPath = Join-Path "$PSScriptRoot\..\..\TestResults" "TEST-$($file.BaseName).xml" 50 | $config.Run.Path = $file.FullName 51 | $config.Run.PassThru = $true 52 | $config.Output.Verbosity = $Output 53 | $results = Invoke-Pester -Configuration $config 54 | foreach ($result in $results) 55 | { 56 | $totalRun += $result.TotalCount 57 | $totalFailed += $result.FailedCount 58 | $result.Tests | Where-Object Result -ne 'Passed' | ForEach-Object { 59 | $testresults += [pscustomobject]@{ 60 | Block = $_.Block 61 | Name = "It $($_.Name)" 62 | Result = $_.Result 63 | Message = $_.ErrorRecord.DisplayErrorMessage 64 | } 65 | } 66 | } 67 | } 68 | } 69 | #endregion Run General Tests 70 | 71 | $global:__pester_data.ScriptAnalyzer | Out-Host 72 | 73 | #region Test Commands 74 | if ($TestFunctions) 75 | { 76 | Write-PSFMessage -Level Important -Message "Proceeding with individual tests" 77 | foreach ($file in (Get-ChildItem "$PSScriptRoot\functions" -Recurse -File | Where-Object Name -like "*Tests.ps1")) 78 | { 79 | if ($file.Name -notlike $Include) { continue } 80 | if ($file.Name -like $Exclude) { continue } 81 | 82 | Write-PSFMessage -Level Significant -Message " Executing $($file.Name)" 83 | $config.TestResult.OutputPath = Join-Path "$PSScriptRoot\..\..\TestResults" "TEST-$($file.BaseName).xml" 84 | $config.Run.Path = $file.FullName 85 | $config.Run.PassThru = $true 86 | $config.Output.Verbosity = $Output 87 | $results = Invoke-Pester -Configuration $config 88 | foreach ($result in $results) 89 | { 90 | $totalRun += $result.TotalCount 91 | $totalFailed += $result.FailedCount 92 | $result.Tests | Where-Object Result -ne 'Passed' | ForEach-Object { 93 | $testresults += [pscustomobject]@{ 94 | Block = $_.Block 95 | Name = "It $($_.Name)" 96 | Result = $_.Result 97 | Message = $_.ErrorRecord.DisplayErrorMessage 98 | } 99 | } 100 | } 101 | } 102 | } 103 | #endregion Test Commands 104 | 105 | $testresults | Sort-Object Describe, Context, Name, Result, Message | Format-List 106 | 107 | if ($totalFailed -eq 0) { Write-PSFMessage -Level Critical -Message "All $totalRun tests executed without a single failure!" } 108 | else { Write-PSFMessage -Level Critical -Message "$totalFailed tests out of $totalRun tests failed!" } 109 | 110 | if ($totalFailed -gt 0) 111 | { 112 | throw "$totalFailed / $totalRun tests failed!" 113 | } 114 | -------------------------------------------------------------------------------- /ADMF/functions/Get-AdmfContext.ps1: -------------------------------------------------------------------------------- 1 | function Get-AdmfContext 2 | { 3 | <# 4 | .SYNOPSIS 5 | Return available contexts. 6 | 7 | .DESCRIPTION 8 | Return available contexts. 9 | By default, only the latest version of any given context will be returned. 10 | 11 | .PARAMETER Name 12 | The name of the context to filter by. 13 | 14 | .PARAMETER Store 15 | The context stores to look in. 16 | 17 | .PARAMETER All 18 | Return all versions of any given context, rather than just the latest version. 19 | 20 | .PARAMETER Current 21 | Displays the currently active contexts. 22 | 23 | .PARAMETER Importing 24 | Return the contexts that are currently being imported. 25 | Use this to react from within your context's scriptblocks to any other context that is selected. 26 | This parameter only has meaning when used within a context's scriptblocks. 27 | 28 | .PARAMETER DomainTable 29 | Return a list of which target domain has which contexts assigned in cache. 30 | 31 | .EXAMPLE 32 | PS C:\> Get-AdmfContext 33 | 34 | Returns the latest version of all available contexts. 35 | #> 36 | [CmdletBinding(DefaultParameterSetName = 'Search')] 37 | param ( 38 | [Parameter(ParameterSetName = 'Search')] 39 | [string] 40 | $Name = '*', 41 | 42 | [Parameter(ParameterSetName = 'Search')] 43 | [string] 44 | $Store = '*', 45 | 46 | [Parameter(ParameterSetName = 'Search')] 47 | [switch] 48 | $All, 49 | 50 | [Parameter(ParameterSetName = 'Current')] 51 | [switch] 52 | $Current, 53 | 54 | [Parameter(ParameterSetName = 'Importing')] 55 | [switch] 56 | $Importing, 57 | 58 | [Parameter(ParameterSetName = 'Server')] 59 | [switch] 60 | $DomainTable 61 | ) 62 | 63 | process 64 | { 65 | if ($Current) 66 | { 67 | return $script:loadedContexts 68 | } 69 | if ($DomainTable) 70 | { 71 | return $script:assignedContexts.Clone() 72 | } 73 | if ($Importing) 74 | { 75 | return (Get-PSFTaskEngineCache -Module ADMF -Name currentlyImportingContexts) 76 | } 77 | $contextStores = Get-AdmfContextStore -Name $Store 78 | $allContextData = foreach ($contextStore in $contextStores) 79 | { 80 | if (-not (Test-Path $contextStore.Path)) { continue } 81 | foreach ($folder in (Get-ChildItem -Path $contextStore.Path -Filter $Name -Directory)) 82 | { 83 | $versionFolders = Get-ChildItem -Path $folder.FullName -Directory | Where-Object { $_.Name -as [version] } | Sort-Object { [version]$_.Name } -Descending 84 | if (-not $All) { $versionFolders = $versionFolders | Select-Object -First 1 } 85 | 86 | foreach ($versionFolder in $versionFolders) 87 | { 88 | $resultObject = [pscustomobject]@{ 89 | PSTypeName = 'ADMF.Context' 90 | Name = $folder.Name 91 | Version = ($versionFolder.Name -as [version]) 92 | Store = $contextStore.Name 93 | Path = $versionFolder.FullName 94 | Description = '' 95 | Weight = 50 96 | Author = '' 97 | Prerequisites = @() 98 | MutuallyExclusive = @() 99 | Group = 'Default' 100 | } 101 | if (Test-Path -Path "$($versionFolder.FullName)\context.json") 102 | { 103 | $contextData = Get-Content -Path "$($versionFolder.FullName)\context.json" | ConvertFrom-Json 104 | if ($contextData.Weight -as [int]) { $resultObject.Weight = $contextData.Weight -as [int] } 105 | if ($contextData.Description) { $resultObject.Description = $contextData.Description } 106 | if ($contextData.Author) { $resultObject.Author = $contextData.Author } 107 | if ($contextData.Prerequisites) { $resultObject.Prerequisites = $contextData.Prerequisites } 108 | if ($contextData.MutuallyExclusive) { $resultObject.MutuallyExclusive = $contextData.MutuallyExclusive } 109 | if ($contextData.Group) { $resultObject.Group = $contextData.Group } 110 | } 111 | 112 | $resultObject 113 | } 114 | } 115 | } 116 | 117 | if ($All) { return $allContextData } 118 | 119 | # Only return highest version if -All has not been set 120 | # The same context name might be stored in multiple stores 121 | $allContextData | Group-Object Name | ForEach-Object { 122 | $_.Group | Sort-Object Version -Descending | Select-Object -First 1 | Select-PSFObject -TypeName 'ADMF.Context' 123 | } 124 | } 125 | } -------------------------------------------------------------------------------- /ADMF/internal/data/context/dc/fsaccessrules/readme.md: -------------------------------------------------------------------------------- 1 | # File System Access Rules 2 | 3 | ## Description 4 | 5 | This Component allows defining the Access Rules on file system paths on DomainControllers. 6 | 7 | By default, defining any explicit access rule will cause all non-defined Access Rules to be removed. 8 | To change this behavior, use the `AccessMode` parameter. 9 | 10 | Inherited rights will always be ignored, and no other folder will be inspected for permissions. 11 | 12 | ## Example Configuration 13 | 14 | Create two access rules: 15 | 16 | + C:\Scripts_old should have no explicit permissions 17 | + C:\Scripts should have the domain's administrator account with FullControl permissions 18 | 19 | ```json 20 | [ 21 | { 22 | "Path": "C:\\Scripts_old", 23 | "Empty": true 24 | }, 25 | { 26 | "Path": "C:\\Scripts", 27 | "Identity": "%DomainName%\\Administrator", 28 | "Rights": "FullControl" 29 | } 30 | ] 31 | ``` 32 | 33 | Create a rule, granting modify access to all child items but not the folder itself to the Domain Admins. 34 | Existing permissions to the Domain Admins are replaced by this new permission. 35 | 36 | ```json 37 | [ 38 | { 39 | "Path": "C:\\Scripts", 40 | "Identity": "%DomainSID%-512", 41 | "Rights": "Modify", 42 | "Propagation": "InheritOnly", 43 | "AccessMode": "Defined" 44 | } 45 | ] 46 | ``` 47 | 48 | ## Tools 49 | 50 | This snippet will parse the non-inherited access rules from the targeted folder: 51 | 52 | ```powershell 53 | Get-Acl C:\Temp | 54 | Select-Object -ExpandProperty Access | 55 | Where-Object IsInherited -eq $false | 56 | Select-PSFObject @( 57 | @{Name="Path";Expression={'C:\Temp'}} 58 | 'FileSystemRights as Rights to String' 59 | 'AccessControlType as Type to String' 60 | @{Name="Identity";Expression={$_.IdentityReference.ToString() -replace '^contoso','%DomainNetBIOSName%'}} 61 | 'InheritanceFlags as Inheritance to String' 62 | 'PropagationFlags as Propagation to String' 63 | ) | ConvertTo-Json 64 | ``` 65 | 66 | > Note: Replace the two instances of the path and the domainname (In this example: contoso) to match your needs. 67 | 68 | ## Parameters 69 | 70 | ### Path 71 | 72 | The path to the filesystem object to grant permissions on. 73 | 74 | > Supports string resolution. 75 | 76 | ### Identity 77 | 78 | What identity / principal to grant access. 79 | 80 | > Supports string resolution. 81 | 82 | ### Rights 83 | 84 | What file system right to grant. 85 | Typical rights include FullControl, Read, ReadWrite, Modify. 86 | 87 | For a full list of rights, see: https://docs.microsoft.com/en-us/dotnet/api/system.security.accesscontrol.filesystemrights 88 | 89 | ### Type 90 | 91 | Whether this is an allow or a deny rule. 92 | Defaults to Allow. 93 | 94 | ### Inheritance 95 | 96 | Who and how are access rules inherited. 97 | Defaults to 'ContainerInherit, ObjectInherit', meaning everything beneath the path inherits as well. 98 | 99 | Options: 100 | 101 | None | None of the children inherits the ACE 102 | ContainerInherit | Only subfolders inherit the ACE 103 | ObjectInherit | Only child items that are files inherit the ACE 104 | 105 | > Each option can be combined with each other 106 | 107 | ### Propagation 108 | 109 | How access rules are being propagated. 110 | Defaults to "None", the windows default behavior. 111 | 112 | Options: 113 | 114 | InheritOnly | Permissions only apply to items under this folder, not the folder itself. 115 | NoPropagateInherit | Permissions only apply to this folder but none of its child items. 116 | None | Permissions apply to this folder and all children. 117 | 118 | ### Empty 119 | 120 | This path should have no explicit ACE defined. 121 | 122 | ### AccessMode 123 | 124 | How filesystem access rules are processed. 125 | Supports three configurations: 126 | 127 | Constrained | The default access mode, will remove any excess access rules. 128 | Additive | Ignore any access rules already on the path, even if not configured 129 | Defined | Ignore any access rules already on the path, even if not configured UNLESS the identity on those rules has an access level defined for it. 130 | 131 | ### ServerRole 132 | 133 | What domain controller to apply this to: 134 | 135 | All | All DCs in the enterprise 136 | FSMO | Only DCs that have any FSMO role 137 | PDC | Only the PDCEmulator 138 | -------------------------------------------------------------------------------- /ADMF/functions/Invoke-AdmfDC.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-AdmfDC 2 | { 3 | <# 4 | .SYNOPSIS 5 | Brings all DCs of the target domain into the desired/defined state. 6 | 7 | .DESCRIPTION 8 | Brings all DCs of the target domain into the desired/defined state. 9 | 10 | .PARAMETER Server 11 | The server / domain to work with. 12 | 13 | .PARAMETER Credential 14 | The credentials to use for this operation. 15 | 16 | .PARAMETER TargetServer 17 | The specific server(s) to process. 18 | If specified, only listed domain controllers will be affected. 19 | Specify the full FQDN of the server. 20 | 21 | .PARAMETER Options 22 | Which aspects to actually update. 23 | By default, all Components are applied. 24 | 25 | .PARAMETER CredentialProvider 26 | The credential provider to use to resolve the input credentials. 27 | See help on Register-AdmfCredentialProvider for details. 28 | 29 | .PARAMETER ContextPrompt 30 | Force displaying the Context selection User Interface. 31 | 32 | .PARAMETER Confirm 33 | If this switch is enabled, you will be prompted for confirmation before executing any operations that change state. 34 | 35 | .PARAMETER WhatIf 36 | If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run. 37 | 38 | .EXAMPLE 39 | PS C:\> Invoke-AdmfDC -Server corp.contoso.com 40 | 41 | Brings all DCs of the domain corp.contoso.com into the desired/defined state. 42 | #> 43 | [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingPlainTextForPassword', '')] 44 | [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')] 45 | param ( 46 | [PSFComputer] 47 | $Server, 48 | 49 | [PSCredential] 50 | $Credential, 51 | 52 | [string[]] 53 | $TargetServer = @(), 54 | 55 | [ADMF.UpdateDCOptions[]] 56 | $Options = 'Default', 57 | 58 | [string] 59 | $CredentialProvider = 'default', 60 | 61 | [Alias('Ctx')] 62 | [switch] 63 | $ContextPrompt 64 | ) 65 | 66 | begin 67 | { 68 | Reset-DomainControllerCache 69 | $parameters = $PSBoundParameters | ConvertTo-PSFHashtable -Include Server, Credential 70 | if (-not $Server -and $TargetServer) { 71 | $parameters.Server = $TargetServer | Select-Object -First 1 72 | } 73 | $originalArgument = Invoke-PreCredentialProvider @parameters -ProviderName $CredentialProvider -Parameter $parameters -Cmdlet $PSCmdlet 74 | try { $dcServer = Resolve-DomainController @parameters -Confirm:$false } 75 | catch 76 | { 77 | Invoke-PostCredentialProvider -ProviderName $CredentialProvider -Server $originalArgument.Server -Credential $originalArgument.Credential -Cmdlet $PSCmdlet 78 | throw 79 | } 80 | $parameters.Server = $dcServer 81 | Invoke-PSFCallback -Data $parameters -EnableException $true -PSCmdlet $PSCmdlet 82 | Set-AdmfContext @parameters -Interactive -ReUse:$(-not $ContextPrompt) -EnableException 83 | $parameters += $PSBoundParameters | ConvertTo-PSFHashtable -Include WhatIf, Confirm, Verbose, Debug 84 | $parameters.Server = $dcServer 85 | [ADMF.UpdateDCOptions]$newOptions = $Options 86 | } 87 | process 88 | { 89 | try 90 | { 91 | if ($newOptions -band [ADMF.UpdateDCOptions]::Share) 92 | { 93 | if (Get-DCShare) 94 | { 95 | Write-PSFMessage -Level Host -String 'Invoke-AdmfDC.Executing.Invoke' -StringValues 'Shares', $parameters.Server 96 | Invoke-DCShare @parameters -TargetServer $TargetServer 97 | } 98 | else { Write-PSFMessage -Level Host -String 'Invoke-AdmfDC.Skipping.Test.NoConfiguration' -StringValues 'Shares' } 99 | } 100 | if ($newOptions -band [ADMF.UpdateDCOptions]::FSAccessRule) 101 | { 102 | if (Get-DCAccessRule) 103 | { 104 | Write-PSFMessage -Level Host -String 'Invoke-AdmfDC.Executing.Invoke' -StringValues 'FSAccessRules', $parameters.Server 105 | Invoke-DCAccessRule @parameters -TargetServer $TargetServer 106 | } 107 | else { Write-PSFMessage -Level Host -String 'Invoke-AdmfDC.Skipping.Test.NoConfiguration' -StringValues 'FSAccessRules' } 108 | } 109 | } 110 | catch { throw } 111 | finally { 112 | Disable-PSFConsoleInterrupt 113 | try { Invoke-PostCredentialProvider -ProviderName $CredentialProvider -Server $originalArgument.Server -Credential $originalArgument.Credential -Cmdlet $PSCmdlet } 114 | finally { Enable-PSFConsoleInterrupt } 115 | } 116 | } 117 | } -------------------------------------------------------------------------------- /ADMF/functions/Export-AdmfGpo.ps1: -------------------------------------------------------------------------------- 1 | function Export-AdmfGpo { 2 | <# 3 | .SYNOPSIS 4 | Creates an export of GPO objects for use in the Domain Management module. 5 | 6 | .DESCRIPTION 7 | Creates an export of GPO objects for use in the Domain Management module. 8 | Use this command to record new GPO data for the module. 9 | 10 | .PARAMETER Path 11 | The path to which to export the GPOs. 12 | 13 | .PARAMETER GpoObject 14 | The GPO objects to export. 15 | Only accepts output of Get-GPO 16 | 17 | .PARAMETER Domain 18 | The domain to export from. 19 | 20 | .PARAMETER ExcludeWmiFilter 21 | Do not export WmiFilter assignments of GPOs 22 | By default, when exporting GPOs, the associated WMi Filter-Name is also exported 23 | 24 | .PARAMETER OldExportMode 25 | How should this command deal with the folders of previous GPO backups? 26 | By default, when detecting the folders of previous GPO backups, this command 27 | will prompt the user, whether to continue, stop or delete & continue. 28 | 29 | Options: 30 | + Interactive (default): Ask the user for a choice, defaulting to keep the folders. 31 | + Delete: Previous backup folders will be deleted without prompting 32 | + Ignore: Previous backup folders will be kept 33 | 34 | .EXAMPLE 35 | PS C:\> Get-GPO -All | Where-Object DisplayName -like 'AD-D-SEC-T0*' | Export-AdmfGpo -Path . 36 | 37 | Exports all GPOs named like 'AD-D-SEC-T0*' to the current path 38 | #> 39 | [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSReviewUnusedParameter", "")] 40 | [CmdletBinding()] 41 | Param ( 42 | [PsfValidateScript('ADMF.Validate.Path', ErrorString = 'ADMF.Validate.Path')] 43 | [Parameter(Mandatory = $true)] 44 | [string] 45 | $Path, 46 | 47 | [PsfValidateScript('ADMF.Validate.Type.Gpo', ErrorString = 'ADMF.Validate.Type.Gpo')] 48 | [Parameter(Mandatory = $true, ValueFromPipeline = $true)] 49 | $GpoObject, 50 | 51 | [string] 52 | $Domain = $env:USERDNSDOMAIN, 53 | 54 | [switch] 55 | $ExcludeWmiFilter, 56 | 57 | [ValidateSet('Interactive', 'Delete', 'Ignore')] 58 | [string] 59 | $OldExportMode = 'Interactive' 60 | ) 61 | 62 | begin { 63 | $resolvedPath = Resolve-PSFPath -Path $Path -Provider FileSystem -SingleItem 64 | 65 | #region Catch Existing GPO Folders 66 | $stop = $false 67 | $gpoFolders = Get-ChildItem -LiteralPath $resolvedPath -Directory | Where-Object Name -Match ([psfrgx]::Guid) 68 | if ($gpoFolders) { 69 | $doDelete = $OldExportMode -eq 'Delete' 70 | if ($OldExportMode -eq 'Interactive') { 71 | $choice = Get-PSFUserChoice -Caption 'Old GPO Backups Found' -Message "#$(@($gpoFolders).Count) probable GPO Backups have been found in the export path - these could create confusion when trying to add the exported data to the Context, potentially including unneeded folders and their content. What should be done with those folders?" -Options @( 72 | 'Ignore' 73 | 'Delete' 74 | 'Stop' 75 | ) 76 | if (2 -eq $choice) { 77 | $stop = $true 78 | return 79 | } 80 | if (1 -eq $choice) { $doDelete = $true } 81 | } 82 | 83 | if ($doDelete) { 84 | $gpoFolders | Remove-Item -Recurse -Force 85 | } 86 | } 87 | #endregion Catch Existing GPO Folders 88 | 89 | $backupCmd = { Backup-GPO -Path $resolvedPath -Domain $Domain } 90 | $backupGPO = $backupCmd.GetSteppablePipeline() 91 | $backupGPO.Begin($true) 92 | 93 | [System.Collections.ArrayList]$gpoData = @() 94 | $exportID = [guid]::NewGuid().ToString() 95 | } 96 | process { 97 | if ($stop) { return } 98 | 99 | foreach ($gpoItem in $GpoObject) { 100 | $exportData = $backupGPO.Process(($gpoItem | Select-PSFObject 'ID as GUID')) 101 | $data = @{ 102 | DisplayName = $gpoItem.DisplayName 103 | Description = $gpoItem.Description 104 | ID = "{$($exportData.ID.ToString().ToUpper())}" 105 | ExportID = $exportID 106 | } 107 | if (-not $ExcludeWmiFilter -and $gpoItem.WmiFilter.Name) { 108 | $data.WmiFilter = $gpoItem.WmiFilter.Name 109 | } 110 | $null = $gpoData.Add([PSCustomObject]$data) 111 | } 112 | } 113 | end { 114 | if ($stop) { return } 115 | 116 | $backupGPO.End() 117 | $gpoData | ConvertTo-Json | Set-Content "$resolvedPath\exportData.json" 118 | 119 | # Remove hidden attribute, to prevent issues with copy over WinRM 120 | foreach ($fsItem in (Get-ChildItem -Path $resolvedPath -Recurse -Force)) { 121 | $fsItem.Attributes = $fsItem.Attributes -band [System.IO.FileAttributes]::Directory 122 | } 123 | } 124 | } 125 | --------------------------------------------------------------------------------