Skip to content

Commit

Permalink
Merge pull request #51 from ActiveDirectoryManagementFramework/develo…
Browse files Browse the repository at this point in the history
…pment

1.13.97
  • Loading branch information
FriedrichWeinmann authored May 16, 2023
2 parents c56e679 + d5ba240 commit aa31041
Show file tree
Hide file tree
Showing 9 changed files with 475 additions and 22 deletions.
16 changes: 10 additions & 6 deletions ADMF/ADMF.psd1
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
RootModule = 'ADMF.psm1'

# Version number of this module.
ModuleVersion = '1.12.90'
ModuleVersion = '1.13.97'

# ID used to uniquely identify this module
GUID = '43f2a890-942f-4dd7-bad0-b774b44ea849'

Expand All @@ -31,10 +31,10 @@
@{ ModuleName = 'string'; ModuleVersion = '1.1.3' }
@{ ModuleName = 'ResolveString'; ModuleVersion = '1.0.0' }
@{ ModuleName = 'Principal'; ModuleVersion = '1.0.0' }
@{ ModuleName = 'ADMF.Core'; ModuleVersion = '1.1.6' }
@{ ModuleName = 'ADMF.Core'; ModuleVersion = '1.1.9' }
@{ ModuleName = 'DCManagement'; ModuleVersion = '1.2.25' }
@{ ModuleName = 'DomainManagement'; ModuleVersion = '1.8.188' }
@{ ModuleName = 'ForestManagement'; ModuleVersion = '1.5.54' }
@{ ModuleName = 'DomainManagement'; ModuleVersion = '1.8.198' }
@{ ModuleName = 'ForestManagement'; ModuleVersion = '1.5.56' }
)

# Assemblies that must be loaded prior to importing this module
Expand All @@ -59,7 +59,9 @@
'Invoke-AdmfForest'
'Invoke-AdmfItem'
'New-AdmfContext'
'New-AdmfContextModule'
'New-AdmfContextStore'
'Publish-AdmfContext'
'Register-AdmfCredentialProvider'
'Set-AdmfContext'
'Test-AdmfDC'
Expand All @@ -74,7 +76,9 @@
# VariablesToExport = ''

# Aliases to export from this module
# AliasesToExport = ''
AliasesToExport = @(
'iai'
)

# List of all modules packaged with this module
# ModuleList = @()
Expand Down
10 changes: 10 additions & 0 deletions ADMF/changelog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
# Changelog

## 1.13.97 (2023-05-16)

- New: Command New-AdmfContextModule - Create an ADMF Client PowerShell module.
- New: Command Publish-AdmfContext - Publishes a Context as a PowerShell Package.
- New: Alias iai - References Invoke-AdmfItem
- New: Configuration Option: ADMF.PowerShellGet.UseV3 - Whether to use PowerShellGet V3 by default or not.
- Upd: Export-AdmfGpo - Support for PowerShell 7
- Upd: Export-AdmfGpo - Added handling for existing GPO backup folders in the export path.
- Fix: Error on PowerShell 7 for all access rule related scans

## 1.12.90 (2023-02-10)

- Fix: Configuration DCSelectionMode - changing the setting will not apply the correct value due to a bug in the validator
Expand Down
65 changes: 52 additions & 13 deletions ADMF/functions/Export-AdmfGpo.ps1
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
function Export-AdmfGpo
{
function Export-AdmfGpo {
<#
.SYNOPSIS
Creates an export of GPO objects for use in the Domain Management module.
Expand All @@ -21,6 +20,16 @@
.PARAMETER ExcludeWmiFilter
Do not export WmiFilter assignments of GPOs
By default, when exporting GPOs, the associated WMi Filter-Name is also exported
.PARAMETER OldExportMode
How should this command deal with the folders of previous GPO backups?
By default, when detecting the folders of previous GPO backups, this command
will prompt the user, whether to continue, stop or delete & continue.
Options:
+ Interactive (default): Ask the user for a choice, defaulting to keep the folders.
+ Delete: Previous backup folders will be deleted without prompting
+ Ignore: Previous backup folders will be kept
.EXAMPLE
PS C:\> Get-GPO -All | Where-Object DisplayName -like 'AD-D-SEC-T0*' | Export-AdmfGpo -Path .
Expand All @@ -43,41 +52,71 @@
$Domain = $env:USERDNSDOMAIN,

[switch]
$ExcludeWmiFilter
$ExcludeWmiFilter,

[ValidateSet('Interactive', 'Delete', 'Ignore')]
[string]
$OldExportMode = 'Interactive'
)

begin
{
begin {
$resolvedPath = Resolve-PSFPath -Path $Path -Provider FileSystem -SingleItem

#region Catch Existing GPO Folders
$stop = $false
$gpoFolders = Get-ChildItem -LiteralPath $resolvedPath -Directory | Where-Object Name -Match ([psfrgx]::Guid)
if ($gpoFolders) {
$doDelete = $OldExportMode -eq 'Delete'
if ($OldExportMode -eq 'Interactive') {
$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 @(
'Ignore'
'Delete'
'Stop'
)
if (2 -eq $choice) {
$stop = $true
return
}
if (1 -eq $choice) { $doDelete = $true }
}

if ($doDelete) {
$gpoFolders | Remove-Item -Recurse -Force
}
}
#endregion Catch Existing GPO Folders

$backupCmd = { Backup-GPO -Path $resolvedPath -Domain $Domain }
$backupGPO = $backupCmd.GetSteppablePipeline()
$backupGPO.Begin($true)

[System.Collections.ArrayList]$gpoData = @()
$exportID = (New-Guid).ToString()
$exportID = [guid]::NewGuid().ToString()
}
process
{
process {
if ($stop) { return }

foreach ($gpoItem in $GpoObject) {
$exportData = $backupGPO.Process(($gpoItem | Select-PSFObject 'ID as GUID'))
$data = @{
DisplayName = $gpoItem.DisplayName
Description = $gpoItem.Description
ID = "{$($exportData.ID.ToString().ToUpper())}"
ExportID = $exportID
ID = "{$($exportData.ID.ToString().ToUpper())}"
ExportID = $exportID
}
if (-not $ExcludeWmiFilter -and $gpoItem.WmiFilter.Name) {
$data.WmiFilter = $gpoItem.WmiFilter.Name
}
$null = $gpoData.Add([PSCustomObject]$data)
}
}
end
{
end {
if ($stop) { return }

$backupGPO.End()
$gpoData | ConvertTo-Json | Set-Content "$resolvedPath\exportData.json"

# Remove hidden attribute, top prevent issues with copy over WinRM
# Remove hidden attribute, to prevent issues with copy over WinRM
foreach ($fsItem in (Get-ChildItem -Path $resolvedPath -Recurse -Force)) {
$fsItem.Attributes = $fsItem.Attributes -band [System.IO.FileAttributes]::Directory
}
Expand Down
1 change: 1 addition & 0 deletions ADMF/functions/Invoke-AdmfItem.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
Apply all create actions for all users and groups in contoso.com.
#>
[Alias('iai')]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingPlainTextForPassword', '')]
[CmdletBinding(SupportsShouldProcess = $true)]
Param (
Expand Down
214 changes: 214 additions & 0 deletions ADMF/functions/New-AdmfContextModule.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
function New-AdmfContextModule {
<#
.SYNOPSIS
Create an ADMF Client PowerShell module.
.DESCRIPTION
Create an ADMF Client PowerShell module.
Specify which contexts to use from a repository to which they have previously been published.
It will then dynamically create a PowerShell module containing all contexts and their
dependencies and publish them under its own Context store once imported.
.PARAMETER Name
Name of the contexts to include.
Supports plain "Name" or the PowerShell module notation.
Examples:
"Default"
@{ ModuleName = 'SecBaseline' }
@{ ModuleName = 'SecBaseline'; ModuleVersion = '2.0.0' }
@{ ModuleName = 'SecBaseline'; RequiredVersion = '2.3.1' }
.PARAMETER Repository
Name of the repository to download from.
.PARAMETER Path
Path where to write the finished module to.
.PARAMETER ModuleName
Name of the module to generate.
.PARAMETER ModuleOption
Additional options to include in the module code when generating the module.
Confirm: Injects all ADMF component modules with the requirement to confirm all changes.
.PARAMETER AliasPrefix
Create aliases for the common ADMF commands.
This simplifies guiding users into loading the module containing their configuration settings.
Rather than having them run Test-ADMFDomain, you could have them run Test-ConDomain,
which would then implicitly load the generated module and make the contexts available,
without having to first manually import the module generated.
.PARAMETER ModuleVersion
The version of the module to generate.
Defaults to 1.0.0
.PARAMETER Credential
Credentials to use for accessing the powershell repository.
.PARAMETER ModuleCode
Additional code to iunclude in the module generated
.PARAMETER GetV3
Use PowerShellGet V3 or later.
Defaults to the configuration setting of ADMF.PowerShellGet.UseV3.
.EXAMPLE
PS C:\> New-AdmfContextModule -Name Default -Repository Contoso -Path . -ModuleName Whatever -ModuleOption Confirm -AliasPrefix WE
Retrieves the "Default" context from the "Contoso" repository.
It will then wrap it into a module named "Whatever", injecting a requirement to confirm all changes and include
aliases for the common ADMF commands with the WE prefix (e.g. Test-WEDomain)
#>
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "")]
[CmdletBinding()]
param (
[Parameter(Mandatory = $true)]
$Name,

[Parameter(Mandatory = $true)]
[string]
$Repository,

[Parameter(Mandatory = $true)]
[string]
$Path,

[Parameter(Mandatory = $true)]
[string]
$ModuleName,

[ValidateSet('Confirm')]
[string[]]
$ModuleOption = 'Confirm',

[string]
$AliasPrefix,

[version]
$ModuleVersion = '1.0.0',

[PSCredential]
$Credential,

[ScriptBlock]
$ModuleCode,

[switch]
$GetV3
)

trap {
Remove-PSFTempItem -ModuleName ADMF -Name *
throw $_
}

# Resolve Target Module Names / Versions
$modules = foreach ($entry in $Name) {
if ($entry -is [string]) {
@{ Name = $entry }
}
else {
$entry | ConvertTo-PSFHashtable Name, ModuleVersion, RequiredVersion
}
}
foreach ($module in $modules) {
if ($module.Name -notlike 'ADMF.Context.*') {
$module.Name = "ADMF.Context.$($module.Name)"
}
if ($GetV3) {
$module.Remove('ModuleVersion')
if ($module.RequiredVersion) {
$module.Version = $module.RequiredVersion
$module.Remove('RequiredVersion')
}
}
else {
if ($module.ModuleVersion) {
$module.MinimumVersion = $module.ModuleVersion
$module.Remove('ModuleVersion')
}
}
}

# Create TEMP folder and deploy them from repository
$folder = New-PSFTempDirectory -Name contextpath -ModuleName ADMF
$moduleParam = $PSBoundParameters | ConvertTo-PSFHashtable -Include Repository, Credential
foreach ($module in $modules) {
if ($GetV3) {
Save-PSResource @moduleParam @module -Path $folder
}
else {
Save-Module @moduleParam @module -Path $folder
}
}

# Create Module & Copy Contexts
$moduleRoot = New-PSFTempDirectory -Name moduleroot -ModuleName ADMF -DirectoryName $ModuleName
$manifest = @{
Path = "$moduleRoot\$ModuleName.psd1"
ModuleVersion = $ModuleVersion
RootModule = "$ModuleName.psm1"
FunctionsToExport = @()
CmdletsToExport = @()
AliasesToExport = @()
VariablesToExport = @()
}
if ($AliasPrefix) {
$manifest.AliasesToExport = "Invoke-$($aliasPrefix)Forest", "Invoke-$($aliasPrefix)Domain", "Invoke-$($aliasPrefix)DC", "Invoke-$($aliasPrefix)Item", "Test-$($aliasPrefix)Domain", "Test-$($aliasPrefix)DC", "Test-$($aliasPrefix)Forest"
}
New-ModuleManifest @manifest
$null = New-Item -Path $moduleRoot -Name "$ModuleName.psm1" -ItemType File
$contextRoot = New-Item -Path $moduleRoot -Name Contexts -ItemType Directory

$contextConfig = Get-Item -Path "$folder/*/*/*/*/context.json"
$contextPaths = $contextConfig.FullName | Split-Path | Split-Path | Sort-Object -Unique
foreach ($contextPath in $contextPaths) {
Copy-Item -Path $contextPath -Destination $contextRoot.FullName -Recurse -Force
}

# Add module content
$content = @()
## Register Context Store
$content += "Set-PSFConfig -FullName 'ADMF.Context.Store.$ModuleName' -Value `"`$PSScriptRoot\Contexts`""
## Confirm Preference
if ($ModuleOption -contains 'Confirm') {
$content += @'
# Confirm by default
& (Get-Module DCManagement) { $script:ConfirmPreference = 'Low' }
& (Get-Module DomainManagement) { $script:ConfirmPreference = 'Low' }
& (Get-Module ForestManagement) { $script:ConfirmPreference = 'Low' }
'@
}
## Add Alias for the ADMF Commands
if ($AliasPrefix) {
$content += "`$aliasPrefix = '$aliasPrefix'"
$content += @'
# Set Aliases for module
$aliases = @{
'Invoke-AdmfDC' = "Invoke-$($aliasPrefix)DC"
'Invoke-AdmfDomain' = "Invoke-$($aliasPrefix)Domain"
'Invoke-AdmfForest' = "Invoke-$($aliasPrefix)Forest"
'Invoke-AdmfItem' = "Invoke-$($aliasPrefix)Item"
'Test-AdmfDC' = "Test-$($aliasPrefix)DC"
'Test-AdmfDomain' = "Test-$($aliasPrefix)Domain"
'Test-AdmfForest' = "Test-$($aliasPrefix)Forest"
}
foreach ($pair in $aliases.GetEnumerator()) {
Set-Alias -Name $pair.Value -Value $pair.Key
}
'@
}
## Add Custom Code
if ($ModuleCode) {
$content += $ModuleCode.ToString()
}
$content | Set-Content -Path "$moduleRoot\$ModuleName.psm1"

# Copy to destination
Copy-Item -Path $moduleRoot -Destination $Path -Recurse


Remove-PSFTempItem -ModuleName ADMF -Name *
}
Loading

0 comments on commit aa31041

Please sign in to comment.