From 0d7b1852b7242aa5b1b5c2c4f96ed4c5c2c445d5 Mon Sep 17 00:00:00 2001 From: Jan-Hendrik Peters Date: Fri, 26 May 2023 14:32:35 +0200 Subject: [PATCH 01/11] Add new AppLocker template --- .../.github/workflows/publish.yml | 34 ++++ .../.github/workflows/validate.yml | 24 +++ templates/AppLockerProject/.gitignore | 2 + templates/AppLockerProject/azurepipelines.yml | 147 ++++++++++++++++++ templates/AppLockerProject/build/build.ps1 | 40 +++++ .../AppLockerProject/build/prerequisites.ps1 | 19 +++ templates/AppLockerProject/build/publish.ps1 | 21 +++ .../build/requiredModules.psd1 | 18 +++ templates/AppLockerProject/build/validate.ps1 | 25 +++ .../configurationdata/Apps/Git.yml | 30 ++++ .../configurationdata/Datum.yml | 58 +++++++ .../configurationdata/Domains/contoso.yml | 47 ++++++ .../configurationdata/Generics/Windows.yml | 21 +++ .../Policies/contoso.com/Pol1.yml | 5 + .../configurationdata/readme.md | 37 +++++ templates/AppLockerProject/readme.md | 31 ++++ .../tests/ConfigurationData/readme.md | 5 + .../tests/Integration/readme.md | 4 + .../AppLockerProject/tests/Unit/readme.md | 3 + 19 files changed, 571 insertions(+) create mode 100644 templates/AppLockerProject/.github/workflows/publish.yml create mode 100644 templates/AppLockerProject/.github/workflows/validate.yml create mode 100644 templates/AppLockerProject/.gitignore create mode 100644 templates/AppLockerProject/azurepipelines.yml create mode 100644 templates/AppLockerProject/build/build.ps1 create mode 100644 templates/AppLockerProject/build/prerequisites.ps1 create mode 100644 templates/AppLockerProject/build/publish.ps1 create mode 100644 templates/AppLockerProject/build/requiredModules.psd1 create mode 100644 templates/AppLockerProject/build/validate.ps1 create mode 100644 templates/AppLockerProject/configurationdata/Apps/Git.yml create mode 100644 templates/AppLockerProject/configurationdata/Datum.yml create mode 100644 templates/AppLockerProject/configurationdata/Domains/contoso.yml create mode 100644 templates/AppLockerProject/configurationdata/Generics/Windows.yml create mode 100644 templates/AppLockerProject/configurationdata/Policies/contoso.com/Pol1.yml create mode 100644 templates/AppLockerProject/configurationdata/readme.md create mode 100644 templates/AppLockerProject/readme.md create mode 100644 templates/AppLockerProject/tests/ConfigurationData/readme.md create mode 100644 templates/AppLockerProject/tests/Integration/readme.md create mode 100644 templates/AppLockerProject/tests/Unit/readme.md diff --git a/templates/AppLockerProject/.github/workflows/publish.yml b/templates/AppLockerProject/.github/workflows/publish.yml new file mode 100644 index 0000000..f9919c0 --- /dev/null +++ b/templates/AppLockerProject/.github/workflows/publish.yml @@ -0,0 +1,34 @@ + +on: + push: + branches: + - main + +jobs: + publish: + + runs-on: windows-latest + + steps: + - uses: actions/checkout@v1 + - name: Install Prerequisites + run: .\build\vsts-prerequisites.ps1 + shell: powershell + - name: Validate Configuration Data + run: .\build\vsts-validate.ps1 -TestType ConfigurationData + shell: powershell + - name: Build + run: .\build\vsts-build.ps1 -IncludeRsop + shell: powershell + - uses: actions/upload-artifact@v3 + with: + name: build-artifacts + path: | + .\output\rsop + .\output\policies + - name: Validate Integration Tests + run: .\build\vsts-validate.ps1 -TestType Integration + shell: powershell + - name: Publish + run: .\build\vsts-publish.ps1 + shell: powershell \ No newline at end of file diff --git a/templates/AppLockerProject/.github/workflows/validate.yml b/templates/AppLockerProject/.github/workflows/validate.yml new file mode 100644 index 0000000..1931439 --- /dev/null +++ b/templates/AppLockerProject/.github/workflows/validate.yml @@ -0,0 +1,24 @@ +on: [pull_request] + +jobs: + validate: + + runs-on: windows-latest + + steps: + - uses: actions/checkout@v1 + - name: Install Prerequisites + run: .\build\vsts-prerequisites.ps1 + shell: powershell + - name: Validate + run: .\build\vsts-validate.ps1 -TestType ConfigurationData + shell: powershell + - name: Build + run: .\build\vsts-build.ps1 -IncludeRsop + shell: powershell + - uses: actions/upload-artifact@v3 + with: + name: build-artifacts + path: | + .\output\rsop + .\output\policies \ No newline at end of file diff --git a/templates/AppLockerProject/.gitignore b/templates/AppLockerProject/.gitignore new file mode 100644 index 0000000..f0d0b64 --- /dev/null +++ b/templates/AppLockerProject/.gitignore @@ -0,0 +1,2 @@ +output +testresults.xml \ No newline at end of file diff --git a/templates/AppLockerProject/azurepipelines.yml b/templates/AppLockerProject/azurepipelines.yml new file mode 100644 index 0000000..f35d562 --- /dev/null +++ b/templates/AppLockerProject/azurepipelines.yml @@ -0,0 +1,147 @@ +trigger: + branches: + include: + - main + +stages: + - stage: build + jobs: + - job: Build + displayName: 'Build AppLocker Artifacts' + pool: + name: Default + workspace: + clean: all + steps: + - task: PowerShell@2 + name: build + displayName: 'Download prerequisites' + inputs: + filePath: '.\build\vsts-prerequisites.ps1' + - task: PowerShell@2 + name: build + displayName: Validate Configuration Data + inputs: + filePath: '.\build\vsts-validate.ps1' + arguments: '-TestType ConfigurationData' + - task: PowerShell@2 + name: build + displayName: Build policy XML + inputs: + filePath: '.\build\vsts-build.ps1' + arguments: '-IncludeRsop' + + - task: PublishBuildArtifacts@1 + displayName: 'Publish Policy XML Files' + inputs: + PathtoPublish: 'output/Policies' + ArtifactName: Policies + + - task: PublishBuildArtifacts@1 + displayName: 'Publish Policy RSOP Files' + inputs: + PathtoPublish: 'output/Rsop' + ArtifactName: Rsop + + - stage: publish + dependsOn: build + jobs: + - deployment: Dev + displayName: Dev Deployment + environment: Dev + pool: + name: Default + workspace: + clean: all + strategy: + runOnce: + deploy: + steps: + - download: None + + - task: DownloadBuildArtifacts@0 + displayName: 'Download Build Artifact: Rsop' + inputs: + buildType: 'current' + artifactName: Rsop + downloadPath: $(Build.SourcesDirectory) + - task: DownloadBuildArtifacts@0 + displayName: 'Download Build Artifact: Policies' + inputs: + buildType: 'current' + artifactName: Policies + downloadPath: $(Build.SourcesDirectory) + - task: PowerShell@2 + name: publishpolicies + displayName: Publish policies + inputs: + filePath: '.\build\vsts-publish.ps1' + arguments: '-OutputPath $(Build.SourcesDirectory)' + + - stage: DscDeploymentTest + dependsOn: + - build + - DscDeploymentDev + jobs: + - deployment: Test + displayName: Test Deployment + environment: Test + pool: + name: Default + workspace: + clean: all + strategy: + runOnce: + deploy: + steps: + - download: None + + - task: DownloadBuildArtifacts@0 + displayName: 'Download Build Artifact: MOF' + inputs: + buildType: 'current' + artifactName: MOF + downloadPath: $(Build.SourcesDirectory) + + - task: CopyFiles@2 + name: DeployMofsToPullServer + displayName: 'Deploy MOF Files to Pull Server' + inputs: + SourceFolder: '$(Build.SourcesDirectory)/MOF/$(Environment.Name)' + Contents: '**' + TargetFolder: '\\dscpull01\DscConfiguration' + OverWrite: true + + - stage: DscDeploymentProd + dependsOn: + - build + - DscDeploymentTest + jobs: + - deployment: Prod + displayName: Prodt Deployment + environment: Prod + pool: + name: Default + workspace: + clean: all + strategy: + runOnce: + deploy: + steps: + - download: None + + - task: DownloadBuildArtifacts@0 + displayName: 'Download Build Artifact: MOF' + inputs: + buildType: 'current' + artifactName: MOF + downloadPath: $(Build.SourcesDirectory) + + - task: CopyFiles@2 + name: DeployMofsToPullServer + displayName: 'Deploy MOF Files to Pull Server' + inputs: + SourceFolder: '$(Build.SourcesDirectory)/MOF/$(Environment.Name)' + Contents: '**' + TargetFolder: '\\dscpull01\DscConfiguration' + OverWrite: true diff --git a/templates/AppLockerProject/build/build.ps1 b/templates/AppLockerProject/build/build.ps1 new file mode 100644 index 0000000..a29cc21 --- /dev/null +++ b/templates/AppLockerProject/build/build.ps1 @@ -0,0 +1,40 @@ + +param +( + [string] + $SourcePath = (Resolve-Path "$PSScriptRoot\..\configurationdata").Path, + + [string] + $OutputPath = (Resolve-Path "$PSScriptRoot\..\output").Path, + + [switch] + $IncludeRsop +) + +$rsopPath = Join-Path -Path $OutputPath -ChildPath rsop +$policyPath = Join-Path -Path $OutputPath -ChildPath policies +if (-not (Test-Path -Path $rsopPath)) +{ + $null = New-Item -Path $rsopPath -ItemType Directory -Force +} + +if (-not (Test-Path -Path $policyPath)) +{ + $null = New-Item -Path $policyPath -ItemType Directory -Force +} + +$datum = New-DatumStructure -DefinitionFile (Join-Path $SourcePath Datum.yml) +$rsops = Get-DatumRsop $datum (Get-DatumNodesRecursive -AllDatumNodes $Datum.AllNodes) +$rsops | Export-AlfXml -Path $policyPath + +if (-not $IncludeRsop) { return } + +foreach ($rsop in $rsops) +{ + $domainPath = Join-Path -Path $rsopPath -ChildPath $rsop.Domain + if (-not (Test-Path -Path $domainPath)) + { + $null = New-Item -Path $domainPath -ItemType Directory -Force + } + $rsop | ConvertTo-Yaml -OutFile (Join-Path -Path $domainPath -ChildPath "$($rsop.PolicyName).yml") -Force +} diff --git a/templates/AppLockerProject/build/prerequisites.ps1 b/templates/AppLockerProject/build/prerequisites.ps1 new file mode 100644 index 0000000..1e909f3 --- /dev/null +++ b/templates/AppLockerProject/build/prerequisites.ps1 @@ -0,0 +1,19 @@ +param +( + [string] + $DependencyPath = (Resolve-Path "$PSScriptRoot\requiredModules.psd1").Path +) + +$psdependConfig = Import-PowerShellDataFile -Path $DependencyPath + +$null = Get-PackageProvider -Name NuGet -ForceBootstrap + +Save-Module -Name PackageManagement, PowerShellGet, PSDepend -Repository $psdependConfig.PSDependOptions.Parameters.Repository -Path $psdependConfig.PSDependOptions.Target -Force + +Remove-Module -Name PowerShellGet -ErrorAction SilentlyContinue -Force +Remove-Module -Name PackageManagement -ErrorAction SilentlyContinue -Force +Import-Module -Force -Name (Join-Path -Path $psdependConfig.PSDependOptions.Target -ChildPath PackageManagement\*\PackageManagement.psd1 -Resolve) +Import-Module -Force -Name (Join-Path -Path $psdependConfig.PSDependOptions.Target -ChildPath PowerShellGet\*\PowerShellGet.psd1 -Resolve) +Import-Module -Name (Join-Path -Path $psdependConfig.PSDependOptions.Target -ChildPath PSDepend\*\PSDepend.psd1 -Resolve) + +Invoke-PSDepend -Path $DependencyPath -Force diff --git a/templates/AppLockerProject/build/publish.ps1 b/templates/AppLockerProject/build/publish.ps1 new file mode 100644 index 0000000..4732f24 --- /dev/null +++ b/templates/AppLockerProject/build/publish.ps1 @@ -0,0 +1,21 @@ +param +( + [string] + $OutputPath = (Resolve-Path "$PSScriptRoot\..\output").Path +) + +foreach ($policy in (Get-ChildItem -Path (Join-Path -Path $OutputPath -ChildPath Policies) -Recurse -Filter *.xml)) +{ + $searcher = [adsisearcher]::new() + $searcher.Filter = "(&(objectClass=groupPolicyContainer)(displayName=$($policy.BaseName)))" + $policyFound = $searcher.FindOne() + + if (-not $policyFound) + { + $null = New-GPO -Name $policy.BaseName -Comment "Auto-updated applocker policy" -Domain $policy.Directory.Name + } + + $policyFound = $searcher.FindOne() + + Set-AppLockerPolicy -XmlPolicy (Get-Content -Path $policy.FullName) -Ldap $policyFound.Path +} diff --git a/templates/AppLockerProject/build/requiredModules.psd1 b/templates/AppLockerProject/build/requiredModules.psd1 new file mode 100644 index 0000000..98f6500 --- /dev/null +++ b/templates/AppLockerProject/build/requiredModules.psd1 @@ -0,0 +1,18 @@ +@{ + PSDependOptions = @{ + AddToPath = $true + Target = 'output\RequiredModules' + Parameters = @{ + Repository = 'PSGallery' + AllowPreRelease = $true + } + } + + 'powershell-yaml' = '0.4.7' + PSScriptAnalyzer = '1.21.0' + Pester = '5.4.1' + 'Sampler.DscPipeline' = '0.2.0-preview0015' # Unfortunately still in preview + Datum = '0.40.1' + 'Datum.InvokeCommand' = '0.3.0' + AppLockerFoundry = '1.1.0' +} diff --git a/templates/AppLockerProject/build/validate.ps1 b/templates/AppLockerProject/build/validate.ps1 new file mode 100644 index 0000000..6b77946 --- /dev/null +++ b/templates/AppLockerProject/build/validate.ps1 @@ -0,0 +1,25 @@ +[CmdletBinding()] +param +( + [string] + $ProjectRoot = (Resolve-Path "$PSScriptRoot\..").Path, + + [ValidateSet('Unit', 'ConfigurationData', 'Integration')] + [string] + $TestType +) + +Import-Module Pester + +$po = [PesterConfiguration]::New() +$po.Run.Path = Join-Path $ProjectRoot "tests/$TestType" +$po.Run.PassThru = $true +$po.Output.Verbosity = 'Detailed' +$po.TestResult.Enabled = $true +$po.TestResult.OutputPath = Join-Path $ProjectRoot 'testresults.xml' +$po.TestResult.OutputFormat = 'NUnit2.5' + +$result = Invoke-Pester -Configuration $po +if ($result.FailedCount -gt 0) { + throw "Pester tests failed" +} diff --git a/templates/AppLockerProject/configurationdata/Apps/Git.yml b/templates/AppLockerProject/configurationdata/Apps/Git.yml new file mode 100644 index 0000000..0f0de16 --- /dev/null +++ b/templates/AppLockerProject/configurationdata/Apps/Git.yml @@ -0,0 +1,30 @@ +Configurations: + - RuleCollections + +RuleCollections: + Exe: + EnforcementMode: AuditOnly + Rules: + - Name: Allow git.exe + Description: Allow all users to run git.exe + Path: '%PROGRAMFILES%\git\cmd\git.exe' + UserOrGroupSid: S-1-1-0 + Action: Allow + - Name: Allow specific hashed git + Description: Allow all users to run git.exe that has a specific hash + Type: SHA256 + Data: "0xC659F4712B60C25E86A927E0C0121C301075F2D0754506140F8B9812B5FDBA3C" + SourceFileName: git.exe + SourceFileLength: 45104 + UserOrGroupSid: S-1-1-0 + Action: Allow + - Name: Allow signed git + Description: Allow all users to run git.exe that was signed by a specific publisher + PublisherName: O=JOHANNES SCHINDELIN, S=NORDRHEIN-WESTFALEN, C=DE + ProductName: GIT + BinaryName: GIT.EXE + BinaryVersionRange: + LowSection: 2.40.1.1 + HighSection: 2.40.1.1 + UserOrGroupSid: S-1-1-0 + Action: Allow diff --git a/templates/AppLockerProject/configurationdata/Datum.yml b/templates/AppLockerProject/configurationdata/Datum.yml new file mode 100644 index 0000000..0c5b1d1 --- /dev/null +++ b/templates/AppLockerProject/configurationdata/Datum.yml @@ -0,0 +1,58 @@ +ResolutionPrecedence: + - AllNodes\$($Node.Domain)\$($Node.PolicyName) + - '[x= { $Node.Apps | Foreach-Object {"Apps\$_"} } =]' + - Domains\$($Node.Domain) + - Generics\Windows + +DatumHandlersThrowOnError: true +DatumHandlers: + Datum.InvokeCommand::InvokeCommand: + SkipDuringLoad: true + +default_lookup_options: MostSpecific + +lookup_options: + Configurations: + merge_basetype_array: Unique + + RuleCollections: + merge_hash: deep + RuleCollections\Exe: + merge_hash: deep + RuleCollections\Exe\Rules: + merge_hash_array: UniqueKeyValTuples + merge_options: + tuple_keys: + - Name + RuleCollections\Msi: + merge_hash: deep + RuleCollections\Msi\Rules: + merge_hash_array: UniqueKeyValTuples + merge_options: + tuple_keys: + - Name + RuleCollections\Dll: + merge_hash: deep + RuleCollections\Dll\Rules: + merge_hash_array: UniqueKeyValTuples + merge_options: + tuple_keys: + - Name + +DatumStructure: + - StoreName: AllNodes + StoreProvider: Datum::File + StoreOptions: + Path: "./Policies" + - StoreName: Generics + StoreProvider: Datum::File + StoreOptions: + Path: "./Generics" + - StoreName: Domains + StoreProvider: Datum::File + StoreOptions: + Path: "./Domains" + - StoreName: Apps + StoreProvider: Datum::File + StoreOptions: + Path: "./Apps" diff --git a/templates/AppLockerProject/configurationdata/Domains/contoso.yml b/templates/AppLockerProject/configurationdata/Domains/contoso.yml new file mode 100644 index 0000000..15eb10a --- /dev/null +++ b/templates/AppLockerProject/configurationdata/Domains/contoso.yml @@ -0,0 +1,47 @@ +Configurations: + - RuleCollections + +RuleCollections: + Msi: + EnforcementMode: AuditOnly + Rules: + - Name: Allow DLLs (Admin) + Description: Allows members of the local Administrators group to load all DLLs. + Path: "*" + UserOrGroupSid: S-1-5-32-544 + Action: Allow + Dll: + EnforcementMode: AuditOnly + Rules: + - Name: Allow msi in CCMCache (Everyone) + Description: Allows everyone to run installer files in the SCCM cache. + Path: '%WINDIR%\ccmcache\*' + UserOrGroupSid: S-1-1-0 + Action: Allow + - Name: Allow DLLs (Everyone Windir) + Description: Allows Everyone to load DLLs located in the Windows folder. + Path: '%WINDIR%\*' + UserOrGroupSid: S-1-1-0 + Action: Allow + Exceptions: + - '%WINDIR%\SomeFolder' + - '%WINDIR%\AnotherFolder' + - Name: Allow DLLs (Everyone Windir) + Description: Allows Everyone to load DLLs located in the Program Files folder. + Path: '%PROGRAMFILES%\*' + UserOrGroupSid: S-1-1-0 + Action: Allow + - Name: Prohibit PowerShell 2 + Description: Explicitly deny signed DLLs needed for PowerShell v2 + Action: Deny + UserOrGroupSid: S-1-1-0 + PublisherName: O=MICROSOFT CORPORATION, L=REDMOND, S=WASHINGTON, C=US + ProductName: MICROSOFT (R) WINDOWS (R) OPERATING SYSTEM + BinaryName: SYSTEM.MANAGEMENT.AUTOMATION.DLL + BinaryVersionRange: + LowSection: "*" + HighSection: 9.9.9.9 + - Name: Prohibit PowerShell 2 unsigned JIT + Description: Explicitly deny signed DLLs needed for PowerShell v2 + Action: Deny + Path: '%WINDIR%\assembly\NativeImages_v2.0.50727_32\System.Management.A#\*' diff --git a/templates/AppLockerProject/configurationdata/Generics/Windows.yml b/templates/AppLockerProject/configurationdata/Generics/Windows.yml new file mode 100644 index 0000000..2e559ab --- /dev/null +++ b/templates/AppLockerProject/configurationdata/Generics/Windows.yml @@ -0,0 +1,21 @@ +Configurations: + - RuleCollections + +RuleCollections: + Dll: + EnforcementMode: AuditOnly + Rules: + - Name: Prohibit PowerShell 2 + Description: Explicitly deny signed DLLs needed for PowerShell v2 + Action: Deny + UserOrGroupSid: S-1-1-0 + PublisherName: O=MICROSOFT CORPORATION, L=REDMOND, S=WASHINGTON, C=US + ProductName: MICROSOFT (R) WINDOWS (R) OPERATING SYSTEM + BinaryName: SYSTEM.MANAGEMENT.AUTOMATION.DLL + BinaryVersionRange: + LowSection: "*" + HighSection: 9.9.9.9 + - Name: Prohibit PowerShell 2 unsigned JIT + Description: Explicitly deny signed DLLs needed for PowerShell v2 + Action: Deny + Path: '%WINDIR%\assembly\NativeImages_v2.0.50727_32\System.Management.A#\*' diff --git a/templates/AppLockerProject/configurationdata/Policies/contoso.com/Pol1.yml b/templates/AppLockerProject/configurationdata/Policies/contoso.com/Pol1.yml new file mode 100644 index 0000000..0cdd1e6 --- /dev/null +++ b/templates/AppLockerProject/configurationdata/Policies/contoso.com/Pol1.yml @@ -0,0 +1,5 @@ +PolicyName: Pol1 +Domain: contoso.com +Apps: + - Git + - Obs \ No newline at end of file diff --git a/templates/AppLockerProject/configurationdata/readme.md b/templates/AppLockerProject/configurationdata/readme.md new file mode 100644 index 0000000..57ad53e --- /dev/null +++ b/templates/AppLockerProject/configurationdata/readme.md @@ -0,0 +1,37 @@ +# Configuration Data + +The `configurationdata` directory contains your overall merging configuration `Datum.yml` as +individual folders described in your merging configuration's Resolution Precedence. This +template assumes: + +```yaml +ResolutionPrecedence: + - AllNodes\$($Node.PolicyName) + - '[x= { $Node.Apps | Foreach-Object {"Apps\$_"} } =]' + - Domains\$($Node.Domain) + - Generics\Windows +``` + +Generic Windows settings are applied first. Those are merged with all domain-specific +settings, taking into consideration which domain the policy is assigned to. Those are then +merged with all App-specific configurations that a policy should contain, and lastly +the policy itself adds its own specific settings like the policy name. + +## Apps + +The idea is to describe each app in a way that AppLocker knows about all required +binaries. Examine the sample App `Git` to learn more. + +## Domains + +The idea is to describe content that is relevant for each domain. + +## Policies + +Grouped by the domain, each policy should be a single yml file that contains +the Name, Domain and subscribed Apps for that policy. + +## Generics + +Currently only one generic configuration is recommended: Windows. This sample already +contains the recommended content and disables PowerShell 2. diff --git a/templates/AppLockerProject/readme.md b/templates/AppLockerProject/readme.md new file mode 100644 index 0000000..6d24416 --- /dev/null +++ b/templates/AppLockerProject/readme.md @@ -0,0 +1,31 @@ +# þnameþ + +Add your project description here. Configuration data can be generated using +the build dependency `AppLockerFoundry`. + +```powershell +Get-ChildItem -Path "C:\Program Files" -Recurse -Filter *.exe | Get-AlfYamlFileInfo +``` + +## Build and release workflow + +The integrated build workflows for GitHub and Azure DevOps (Server) can be +used out-of-the-box, nearly. If you want to publish your policies in a domain +environment, you will ned to run your build worker with an account +that is capable of updating the required policy objects. This is +due to constraints with the AppLocker cmdlets and their missing capability to specify +alternative credentials. + +The ideal workflow should you need or want to build it your self would look like this: + +1. Ensure prerequisites `build\prerequisites.ps1` +1. Validate Configuration Data: `build\validate.ps1 -TestType ConfigurationData` +1. Build policies and optional RSOP from configuration data: `build\build.ps1 -IncludeRsop` +1. Validate Integration into environment: `build\validate.ps1 -TestType Integration` +1. Publish: `build\publish.ps1` + +## Advanced setup + +Most CI tools support a concept like Environments to which you can attach certain +gates like a manual approval or a scheduled release in a specific time frame. Play +around with this a little bit to further improve your AppLocker pipeline. diff --git a/templates/AppLockerProject/tests/ConfigurationData/readme.md b/templates/AppLockerProject/tests/ConfigurationData/readme.md new file mode 100644 index 0000000..5373f6f --- /dev/null +++ b/templates/AppLockerProject/tests/ConfigurationData/readme.md @@ -0,0 +1,5 @@ +# Configuration Data Tests + +These tests are a bit special, as they don't really fit into unit or integration. These +tests are supposed to ensure the validitity of your configuration data that in the +end make up your policy definitions. \ No newline at end of file diff --git a/templates/AppLockerProject/tests/Integration/readme.md b/templates/AppLockerProject/tests/Integration/readme.md new file mode 100644 index 0000000..5bc1cc7 --- /dev/null +++ b/templates/AppLockerProject/tests/Integration/readme.md @@ -0,0 +1,4 @@ +# Integration and QA tests + +Put your test scripts here that test in a test environment. For example, +this could include linking and validating your new policies. diff --git a/templates/AppLockerProject/tests/Unit/readme.md b/templates/AppLockerProject/tests/Unit/readme.md new file mode 100644 index 0000000..66cddf8 --- /dev/null +++ b/templates/AppLockerProject/tests/Unit/readme.md @@ -0,0 +1,3 @@ +# Unit tests + +Test your functional units here! \ No newline at end of file From 53000866446a6c3e205b30f9702a163c98ec4cba Mon Sep 17 00:00:00 2001 From: Jan-Hendrik Peters Date: Fri, 16 Jun 2023 15:44:11 +0200 Subject: [PATCH 02/11] Add preliminary tests --- templates/AppLockerProject/build/validate.ps1 | 5 ++-- .../ConfigurationData/FileIntegrity.tests.ps1 | 28 +++++++++++++++++++ 2 files changed, 31 insertions(+), 2 deletions(-) create mode 100644 templates/AppLockerProject/tests/ConfigurationData/FileIntegrity.tests.ps1 diff --git a/templates/AppLockerProject/build/validate.ps1 b/templates/AppLockerProject/build/validate.ps1 index 6b77946..04c8ddd 100644 --- a/templates/AppLockerProject/build/validate.ps1 +++ b/templates/AppLockerProject/build/validate.ps1 @@ -11,12 +11,13 @@ param Import-Module Pester +$global:testroot = Join-Path $ProjectRoot tests $po = [PesterConfiguration]::New() -$po.Run.Path = Join-Path $ProjectRoot "tests/$TestType" +$po.Run.Path = Join-Path $global:testroot $TestType $po.Run.PassThru = $true $po.Output.Verbosity = 'Detailed' $po.TestResult.Enabled = $true -$po.TestResult.OutputPath = Join-Path $ProjectRoot 'testresults.xml' +$po.TestResult.OutputPath = Join-Path $global:testroot 'testresults.xml' $po.TestResult.OutputFormat = 'NUnit2.5' $result = Invoke-Pester -Configuration $po diff --git a/templates/AppLockerProject/tests/ConfigurationData/FileIntegrity.tests.ps1 b/templates/AppLockerProject/tests/ConfigurationData/FileIntegrity.tests.ps1 new file mode 100644 index 0000000..7a4d32f --- /dev/null +++ b/templates/AppLockerProject/tests/ConfigurationData/FileIntegrity.tests.ps1 @@ -0,0 +1,28 @@ +BeforeDiscovery { + $yamlFiles = Get-ChildItem -Path "$global:testroot\..\configurationdata" -Filter *.ym*l -Recurse -File | Foreach-Object { + @{ + FullName = $_.FullName + BaseName = $_.BaseName + Name = $_.Name + } + } +} + +Describe "YAML file integrity" { + It " Convert from YAML without errors" -TestCases $yamlFiles { + { Get-Content -Raw -Path $FullName | ConvertFrom-Yaml -ErrorAction Stop } | Should -Not -Throw + } + + It " Contains only valid rule types" -TestCases $yamlFiles { + $types = 'Dll', 'Exe', 'Msi', 'Script', 'Appx' + + $content = Get-Content -Raw -Path $FullName | ConvertFrom-Yaml -ErrorAction SilentlyContinue + + if (-not $content.ContainsKey('RuleCollections')) + { + return + } + + $content.RuleCollections.Keys | Foreach-Object { $_ | Should -BeIn $types } + } +} From 49cab448f2c7a5f02954374053df4d79e77d0dae Mon Sep 17 00:00:00 2001 From: Jan-Hendrik Peters Date: Fri, 16 Jun 2023 16:56:07 +0200 Subject: [PATCH 03/11] Add parameters --- templates/AppLockerProject/PSMDInvoke.ps1 | 5 +++++ templates/AppLockerProject/PSMDTemplate.ps1 | 11 +++++++++++ .../Domains/\303\276domainfqdn\303\276.yml" | 0 .../Policies/\303\276domainfqdn\303\276/Pol1.yml" | 2 +- templates/AppLockerProject/readme.md | 10 +++++----- 5 files changed, 22 insertions(+), 6 deletions(-) create mode 100644 templates/AppLockerProject/PSMDInvoke.ps1 create mode 100644 templates/AppLockerProject/PSMDTemplate.ps1 rename templates/AppLockerProject/configurationdata/Domains/contoso.yml => "templates/AppLockerProject/configurationdata/Domains/\303\276domainfqdn\303\276.yml" (100%) rename templates/AppLockerProject/configurationdata/Policies/contoso.com/Pol1.yml => "templates/AppLockerProject/configurationdata/Policies/\303\276domainfqdn\303\276/Pol1.yml" (62%) diff --git a/templates/AppLockerProject/PSMDInvoke.ps1 b/templates/AppLockerProject/PSMDInvoke.ps1 new file mode 100644 index 0000000..17f4c5c --- /dev/null +++ b/templates/AppLockerProject/PSMDInvoke.ps1 @@ -0,0 +1,5 @@ +param ( + $Path +) + +New-PSMDTemplate -ReferencePath "$PSScriptRoot" -OutPath $Path \ No newline at end of file diff --git a/templates/AppLockerProject/PSMDTemplate.ps1 b/templates/AppLockerProject/PSMDTemplate.ps1 new file mode 100644 index 0000000..b5ec0e6 --- /dev/null +++ b/templates/AppLockerProject/PSMDTemplate.ps1 @@ -0,0 +1,11 @@ +@{ + TemplateName = 'AppLockerProject' + Version = "1.0.0" + AutoIncrementVersion = $true + Tags = 'module','psframework', 'applocker' + Author = 'Jan-Hendrik Peters' + Description = 'PowerShell Framework based AppLocker CI template' + Exclusions = @("PSMDInvoke.ps1", ".PSMDDependency") # Contains list of files - relative path to root - to ignore when building the template + Scripts = @{ } + NoFolder = $true +} \ No newline at end of file diff --git a/templates/AppLockerProject/configurationdata/Domains/contoso.yml "b/templates/AppLockerProject/configurationdata/Domains/\303\276domainfqdn\303\276.yml" similarity index 100% rename from templates/AppLockerProject/configurationdata/Domains/contoso.yml rename to "templates/AppLockerProject/configurationdata/Domains/\303\276domainfqdn\303\276.yml" diff --git a/templates/AppLockerProject/configurationdata/Policies/contoso.com/Pol1.yml "b/templates/AppLockerProject/configurationdata/Policies/\303\276domainfqdn\303\276/Pol1.yml" similarity index 62% rename from templates/AppLockerProject/configurationdata/Policies/contoso.com/Pol1.yml rename to "templates/AppLockerProject/configurationdata/Policies/\303\276domainfqdn\303\276/Pol1.yml" index 0cdd1e6..c737ada 100644 --- a/templates/AppLockerProject/configurationdata/Policies/contoso.com/Pol1.yml +++ "b/templates/AppLockerProject/configurationdata/Policies/\303\276domainfqdn\303\276/Pol1.yml" @@ -1,5 +1,5 @@ PolicyName: Pol1 -Domain: contoso.com +Domain: þdomainfqdnþ Apps: - Git - Obs \ No newline at end of file diff --git a/templates/AppLockerProject/readme.md b/templates/AppLockerProject/readme.md index 6d24416..748c036 100644 --- a/templates/AppLockerProject/readme.md +++ b/templates/AppLockerProject/readme.md @@ -18,11 +18,11 @@ alternative credentials. The ideal workflow should you need or want to build it your self would look like this: -1. Ensure prerequisites `build\prerequisites.ps1` -1. Validate Configuration Data: `build\validate.ps1 -TestType ConfigurationData` -1. Build policies and optional RSOP from configuration data: `build\build.ps1 -IncludeRsop` -1. Validate Integration into environment: `build\validate.ps1 -TestType Integration` -1. Publish: `build\publish.ps1` +1. Ensure prerequisites `.\build\prerequisites.ps1` +1. Validate Configuration Data: `.\build\validate.ps1 -TestType ConfigurationData` +1. Build policies and optional RSOP from configuration data: `.\build\build.ps1 -IncludeRsop` +1. Validate Integration into environment: `.\build\validate.ps1 -TestType Integration` +1. Publish: `.\build\publish.ps1` ## Advanced setup From bb0fff9cbcbae93ead6b9603908ba874a0423255 Mon Sep 17 00:00:00 2001 From: Jan-Hendrik Peters Date: Fri, 16 Jun 2023 17:42:31 +0200 Subject: [PATCH 04/11] Update build template --- templates/AppLockerProject/build/build.ps1 | 19 +++++++++++++-- .../configurationdata/Datum.yml | 24 +++++++++++++++++++ 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/templates/AppLockerProject/build/build.ps1 b/templates/AppLockerProject/build/build.ps1 index a29cc21..5a86cfb 100644 --- a/templates/AppLockerProject/build/build.ps1 +++ b/templates/AppLockerProject/build/build.ps1 @@ -2,15 +2,25 @@ param ( [string] - $SourcePath = (Resolve-Path "$PSScriptRoot\..\configurationdata").Path, + $SourcePath = "$PSScriptRoot\..\configurationdata", [string] - $OutputPath = (Resolve-Path "$PSScriptRoot\..\output").Path, + $OutputPath = "$PSScriptRoot\..\output", [switch] $IncludeRsop ) +$SourcePath = Resolve-Path -Path $SourcePath -ErrorAction Stop +$OutputPath = if (-not (Resolve-Path -Path $OutputPath -ErrorAction SilentlyContinue)) +{ + (New-Item -Path $OutputPath -ItemType Directory -Force).FullName +} +else +{ + Resolve-Path -Path $OutputPath +} + $rsopPath = Join-Path -Path $OutputPath -ChildPath rsop $policyPath = Join-Path -Path $OutputPath -ChildPath policies if (-not (Test-Path -Path $rsopPath)) @@ -23,6 +33,11 @@ if (-not (Test-Path -Path $policyPath)) $null = New-Item -Path $policyPath -ItemType Directory -Force } +if (Get-DatumRsopCache) +{ + Clear-DatumRsopCache +} + $datum = New-DatumStructure -DefinitionFile (Join-Path $SourcePath Datum.yml) $rsops = Get-DatumRsop $datum (Get-DatumNodesRecursive -AllDatumNodes $Datum.AllNodes) $rsops | Export-AlfXml -Path $policyPath diff --git a/templates/AppLockerProject/configurationdata/Datum.yml b/templates/AppLockerProject/configurationdata/Datum.yml index 0c5b1d1..86cb28b 100644 --- a/templates/AppLockerProject/configurationdata/Datum.yml +++ b/templates/AppLockerProject/configurationdata/Datum.yml @@ -24,6 +24,8 @@ lookup_options: merge_options: tuple_keys: - Name + RuleCollections\Exe\Rules\Exceptions: + merge_basetype_array: Unique RuleCollections\Msi: merge_hash: deep RuleCollections\Msi\Rules: @@ -31,6 +33,8 @@ lookup_options: merge_options: tuple_keys: - Name + RuleCollections\Msi\Rules\Exceptions: + merge_basetype_array: Unique RuleCollections\Dll: merge_hash: deep RuleCollections\Dll\Rules: @@ -38,6 +42,26 @@ lookup_options: merge_options: tuple_keys: - Name + RuleCollections\Dll\Rules\Exceptions: + merge_basetype_array: Unique + RuleCollections\Script: + merge_hash: deep + RuleCollections\Script\Rules: + merge_hash_array: UniqueKeyValTuples + merge_options: + tuple_keys: + - Name + RuleCollections\Script\Rules\Exceptions: + merge_basetype_array: Unique + RuleCollections\Appx: + merge_hash: deep + RuleCollections\Appx\Rules: + merge_hash_array: UniqueKeyValTuples + merge_options: + tuple_keys: + - Name + RuleCollections\Appx\Rules\Exceptions: + merge_basetype_array: Unique DatumStructure: - StoreName: AllNodes From 2cbca67dcaef12fee4d1e0516987b066171bf01c Mon Sep 17 00:00:00 2001 From: Jan-Hendrik Peters Date: Sat, 17 Jun 2023 07:49:03 +0200 Subject: [PATCH 05/11] Add RSOP test --- templates/AppLockerProject/build/publish.ps1 | 2 +- .../Domains/\303\276domainfqdn\303\276.yml" | 1 + .../configurationdata/Generics/Windows.yml | 1 + .../tests/ConfigurationData/Rsop.tests.ps1 | 15 +++++++++++++++ 4 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 templates/AppLockerProject/tests/ConfigurationData/Rsop.tests.ps1 diff --git a/templates/AppLockerProject/build/publish.ps1 b/templates/AppLockerProject/build/publish.ps1 index 4732f24..ae08da4 100644 --- a/templates/AppLockerProject/build/publish.ps1 +++ b/templates/AppLockerProject/build/publish.ps1 @@ -17,5 +17,5 @@ foreach ($policy in (Get-ChildItem -Path (Join-Path -Path $OutputPath -ChildPath $policyFound = $searcher.FindOne() - Set-AppLockerPolicy -XmlPolicy (Get-Content -Path $policy.FullName) -Ldap $policyFound.Path + Set-AppLockerPolicy -XmlPolicy $policy.FullName -Ldap $policyFound.Path } diff --git "a/templates/AppLockerProject/configurationdata/Domains/\303\276domainfqdn\303\276.yml" "b/templates/AppLockerProject/configurationdata/Domains/\303\276domainfqdn\303\276.yml" index 15eb10a..cfcd043 100644 --- "a/templates/AppLockerProject/configurationdata/Domains/\303\276domainfqdn\303\276.yml" +++ "b/templates/AppLockerProject/configurationdata/Domains/\303\276domainfqdn\303\276.yml" @@ -44,4 +44,5 @@ RuleCollections: - Name: Prohibit PowerShell 2 unsigned JIT Description: Explicitly deny signed DLLs needed for PowerShell v2 Action: Deny + UserOrGroupSid: S-1-1-0 Path: '%WINDIR%\assembly\NativeImages_v2.0.50727_32\System.Management.A#\*' diff --git a/templates/AppLockerProject/configurationdata/Generics/Windows.yml b/templates/AppLockerProject/configurationdata/Generics/Windows.yml index 2e559ab..c559f35 100644 --- a/templates/AppLockerProject/configurationdata/Generics/Windows.yml +++ b/templates/AppLockerProject/configurationdata/Generics/Windows.yml @@ -16,6 +16,7 @@ RuleCollections: LowSection: "*" HighSection: 9.9.9.9 - Name: Prohibit PowerShell 2 unsigned JIT + UserOrGroupSid: S-1-1-0 Description: Explicitly deny signed DLLs needed for PowerShell v2 Action: Deny Path: '%WINDIR%\assembly\NativeImages_v2.0.50727_32\System.Management.A#\*' diff --git a/templates/AppLockerProject/tests/ConfigurationData/Rsop.tests.ps1 b/templates/AppLockerProject/tests/ConfigurationData/Rsop.tests.ps1 new file mode 100644 index 0000000..b4998ba --- /dev/null +++ b/templates/AppLockerProject/tests/ConfigurationData/Rsop.tests.ps1 @@ -0,0 +1,15 @@ +BeforeDiscovery { + if (Get-DatumRsopCache) + { + Clear-DatumRsopCache + } + + $datum = New-DatumStructure -DefinitionFile (Join-Path "$global:testroot\..\configurationdata" Datum.yml) + [hashtable[]] $rsops = (Get-DatumRsop $datum (Get-DatumNodesRecursive -AllDatumNodes $Datum.AllNodes)).RuleCollections.Values.Rules +} + +Describe "RSOP correctness" { + It " Policy rule has SID" -TestCases $rsops { + $UserOrGroupSid | Should -Not -BeNullOrEmpty + } +} From d690630c7e6bb7dd10f7c2ca10a85b8f66382cd1 Mon Sep 17 00:00:00 2001 From: Jan-Hendrik Peters Date: Sat, 17 Jun 2023 11:01:58 +0200 Subject: [PATCH 06/11] Add integration test template --- .../AppLockerProject/build/prerequisites.ps1 | 25 +++++++++++----- .../build/requiredModules.psd1 | 2 +- .../tests/Integration/GpoExists.tests.ps1 | 29 +++++++++++++++++++ 3 files changed, 47 insertions(+), 9 deletions(-) create mode 100644 templates/AppLockerProject/tests/Integration/GpoExists.tests.ps1 diff --git a/templates/AppLockerProject/build/prerequisites.ps1 b/templates/AppLockerProject/build/prerequisites.ps1 index 1e909f3..188c977 100644 --- a/templates/AppLockerProject/build/prerequisites.ps1 +++ b/templates/AppLockerProject/build/prerequisites.ps1 @@ -1,19 +1,28 @@ param ( [string] - $DependencyPath = (Resolve-Path "$PSScriptRoot\requiredModules.psd1").Path + $DependencyPath = (Resolve-Path "$PSScriptRoot\requiredModules.psd1").Path, + + [switch] + $BuildWorker ) $psdependConfig = Import-PowerShellDataFile -Path $DependencyPath -$null = Get-PackageProvider -Name NuGet -ForceBootstrap +if ($BuildWorker.IsPresent) +{ + $null = Get-PackageProvider -Name NuGet -ForceBootstrap -Save-Module -Name PackageManagement, PowerShellGet, PSDepend -Repository $psdependConfig.PSDependOptions.Parameters.Repository -Path $psdependConfig.PSDependOptions.Target -Force + Install-Module -Force -Name PackageManagement, PowerShellGet -Repository $psdependConfig.PSDependOptions.Parameters.Repository -Scope CurrentUser -Remove-Module -Name PowerShellGet -ErrorAction SilentlyContinue -Force -Remove-Module -Name PackageManagement -ErrorAction SilentlyContinue -Force -Import-Module -Force -Name (Join-Path -Path $psdependConfig.PSDependOptions.Target -ChildPath PackageManagement\*\PackageManagement.psd1 -Resolve) -Import-Module -Force -Name (Join-Path -Path $psdependConfig.PSDependOptions.Target -ChildPath PowerShellGet\*\PowerShellGet.psd1 -Resolve) -Import-Module -Name (Join-Path -Path $psdependConfig.PSDependOptions.Target -ChildPath PSDepend\*\PSDepend.psd1 -Resolve) + Remove-Module -Name PowerShellGet -ErrorAction SilentlyContinue -Force + Remove-Module -Name PackageManagement -ErrorAction SilentlyContinue -Force + Import-Module -Force -Name PowerShellGet + Import-Module -Force -Name PackageManagement + $null = Install-WindowsFeature -Name GPMC +} + +Save-Module -Name PSDepend -Repository $psdependConfig.PSDependOptions.Parameters.Repository -Path $psdependConfig.PSDependOptions.Target -Force +Import-Module -Name (Join-Path -Path $psdependConfig.PSDependOptions.Target -ChildPath PSDepend\*\PSDepend.psd1 -Resolve) Invoke-PSDepend -Path $DependencyPath -Force diff --git a/templates/AppLockerProject/build/requiredModules.psd1 b/templates/AppLockerProject/build/requiredModules.psd1 index 98f6500..4e0380e 100644 --- a/templates/AppLockerProject/build/requiredModules.psd1 +++ b/templates/AppLockerProject/build/requiredModules.psd1 @@ -14,5 +14,5 @@ 'Sampler.DscPipeline' = '0.2.0-preview0015' # Unfortunately still in preview Datum = '0.40.1' 'Datum.InvokeCommand' = '0.3.0' - AppLockerFoundry = '1.1.0' + AppLockerFoundry = '1.1.5' } diff --git a/templates/AppLockerProject/tests/Integration/GpoExists.tests.ps1 b/templates/AppLockerProject/tests/Integration/GpoExists.tests.ps1 new file mode 100644 index 0000000..45b5ddb --- /dev/null +++ b/templates/AppLockerProject/tests/Integration/GpoExists.tests.ps1 @@ -0,0 +1,29 @@ +BeforeDiscovery { + if (Get-DatumRsopCache) + { + Clear-DatumRsopCache + } + + $policies = foreach ($file in (Get-ChildItem -Path (Resolve-Path "$global:testroot\..\configurationdata\Policies").Path -Recurse -Filter *.y*ml -File)) + { + @{ + Name = $file.BaseName + Domain = $file.Directory.Name + } + } +} + +Describe "Policy exists" { + It " Policy exists in " -TestCases $policies { + $gpo = Get-GPO -Name $Name -Domain $Domain + $ctx = [System.DirectoryServices.ActiveDirectory.DirectoryContext]::new('Domain', $Domain) + $domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetDomain($ctx) + $domainDn = $domain.GetDirectoryEntry().DistinguishedName + $appLockerGpo = try + { + Get-AppLockerPolicy -Domain -Ldap "LDAP://CN={$($gpo.Id)},CN=Policies,CN=System,$domainDn" -ErrorAction Stop + } + catch {} # Suppress exception from misbehaving cmdlet that does not like SilentlyContinue + $appLockerGpo.RuleCollections | Should -Not -BeNullOrEmpty + } +} From 45bcebe95d07a5f1fccfcba01ad1b4305aabd21e Mon Sep 17 00:00:00 2001 From: Jan-Hendrik Peters Date: Sat, 17 Jun 2023 11:17:57 +0200 Subject: [PATCH 07/11] Update pipeline --- templates/AppLockerProject/azurepipelines.yml | 148 +++++------------- 1 file changed, 41 insertions(+), 107 deletions(-) diff --git a/templates/AppLockerProject/azurepipelines.yml b/templates/AppLockerProject/azurepipelines.yml index f35d562..40ceb02 100644 --- a/templates/AppLockerProject/azurepipelines.yml +++ b/templates/AppLockerProject/azurepipelines.yml @@ -1,124 +1,50 @@ trigger: branches: include: - - main + - main stages: - stage: build jobs: - job: Build - displayName: 'Build AppLocker Artifacts' + displayName: "Build AppLocker Artifacts" pool: name: Default workspace: clean: all steps: - task: PowerShell@2 - name: build - displayName: 'Download prerequisites' + name: prereq + displayName: "Download prerequisites" inputs: - filePath: '.\build\vsts-prerequisites.ps1' + filePath: '.\build\prerequisites.ps1' - task: PowerShell@2 - name: build + name: validateyaml displayName: Validate Configuration Data inputs: - filePath: '.\build\vsts-validate.ps1' - arguments: '-TestType ConfigurationData' + filePath: '.\build\validate.ps1' + arguments: "-TestType ConfigurationData" - task: PowerShell@2 - name: build + name: buildpolicy displayName: Build policy XML inputs: - filePath: '.\build\vsts-build.ps1' - arguments: '-IncludeRsop' - + filePath: '.\build\build.ps1' + arguments: "-IncludeRsop" - task: PublishBuildArtifacts@1 - displayName: 'Publish Policy XML Files' + displayName: "Publish Policy XML Files" inputs: - PathtoPublish: 'output/Policies' + PathtoPublish: "output/Policies" ArtifactName: Policies - - task: PublishBuildArtifacts@1 - displayName: 'Publish Policy RSOP Files' + displayName: "Publish Policy RSOP Files" inputs: - PathtoPublish: 'output/Rsop' + PathtoPublish: "output/Rsop" ArtifactName: Rsop - - stage: publish dependsOn: build - jobs: - - deployment: Dev - displayName: Dev Deployment - environment: Dev - pool: - name: Default - workspace: - clean: all - strategy: - runOnce: - deploy: - steps: - - download: None - - - task: DownloadBuildArtifacts@0 - displayName: 'Download Build Artifact: Rsop' - inputs: - buildType: 'current' - artifactName: Rsop - downloadPath: $(Build.SourcesDirectory) - - task: DownloadBuildArtifacts@0 - displayName: 'Download Build Artifact: Policies' - inputs: - buildType: 'current' - artifactName: Policies - downloadPath: $(Build.SourcesDirectory) - - task: PowerShell@2 - name: publishpolicies - displayName: Publish policies - inputs: - filePath: '.\build\vsts-publish.ps1' - arguments: '-OutputPath $(Build.SourcesDirectory)' - - - stage: DscDeploymentTest - dependsOn: - - build - - DscDeploymentDev - jobs: - - deployment: Test - displayName: Test Deployment - environment: Test - pool: - name: Default - workspace: - clean: all - strategy: - runOnce: - deploy: - steps: - - download: None - - - task: DownloadBuildArtifacts@0 - displayName: 'Download Build Artifact: MOF' - inputs: - buildType: 'current' - artifactName: MOF - downloadPath: $(Build.SourcesDirectory) - - - task: CopyFiles@2 - name: DeployMofsToPullServer - displayName: 'Deploy MOF Files to Pull Server' - inputs: - SourceFolder: '$(Build.SourcesDirectory)/MOF/$(Environment.Name)' - Contents: '**' - TargetFolder: '\\dscpull01\DscConfiguration' - OverWrite: true - - - stage: DscDeploymentProd - dependsOn: - - build - - DscDeploymentTest jobs: - deployment: Prod - displayName: Prodt Deployment + displayName: Prod Deployment environment: Prod pool: name: Default @@ -128,20 +54,28 @@ stages: runOnce: deploy: steps: - - download: None - - - task: DownloadBuildArtifacts@0 - displayName: 'Download Build Artifact: MOF' - inputs: - buildType: 'current' - artifactName: MOF - downloadPath: $(Build.SourcesDirectory) - - - task: CopyFiles@2 - name: DeployMofsToPullServer - displayName: 'Deploy MOF Files to Pull Server' - inputs: - SourceFolder: '$(Build.SourcesDirectory)/MOF/$(Environment.Name)' - Contents: '**' - TargetFolder: '\\dscpull01\DscConfiguration' - OverWrite: true + - download: None + - task: DownloadBuildArtifacts@0 + displayName: "Download Build Artifact: Rsop" + inputs: + buildType: "current" + artifactName: Rsop + downloadPath: $(Build.SourcesDirectory) + - task: DownloadBuildArtifacts@0 + displayName: "Download Build Artifact: Policies" + inputs: + buildType: "current" + artifactName: Policies + downloadPath: $(Build.SourcesDirectory) + - task: PowerShell@2 + name: publishpolicies + displayName: Publish policies + inputs: + filePath: '.\build\publish.ps1' + arguments: "-OutputPath $(Build.SourcesDirectory)" + - task: PowerShell@2 + name: validateintegration + displayName: Validate Integration + inputs: + filePath: '.\build\validate.ps1' + arguments: "-TestType Integration" From 4e3f0613d4093d4f998f6790aa0772ecd5d9807b Mon Sep 17 00:00:00 2001 From: Jan-Hendrik Peters Date: Sat, 17 Jun 2023 11:27:34 +0200 Subject: [PATCH 08/11] Update module path --- templates/AppLockerProject/azurepipelines.yml | 72 ++++++++++--------- templates/AppLockerProject/build/build.ps1 | 17 ++++- templates/AppLockerProject/build/publish.ps1 | 11 +++ .../build/requiredModules.psd1 | 2 +- templates/AppLockerProject/build/validate.ps1 | 13 +++- 5 files changed, 78 insertions(+), 37 deletions(-) diff --git a/templates/AppLockerProject/azurepipelines.yml b/templates/AppLockerProject/azurepipelines.yml index 40ceb02..c7add4e 100644 --- a/templates/AppLockerProject/azurepipelines.yml +++ b/templates/AppLockerProject/azurepipelines.yml @@ -1,13 +1,13 @@ trigger: branches: include: - - main + - main stages: - stage: build jobs: - job: Build - displayName: "Build AppLocker Artifacts" + displayName: 'Build AppLocker Artifacts' pool: name: Default workspace: @@ -15,30 +15,33 @@ stages: steps: - task: PowerShell@2 name: prereq - displayName: "Download prerequisites" + displayName: 'Download prerequisites' inputs: filePath: '.\build\prerequisites.ps1' + arguments: '-DependencyPath .\build\requiredModules.psd1' - task: PowerShell@2 name: validateyaml displayName: Validate Configuration Data inputs: filePath: '.\build\validate.ps1' - arguments: "-TestType ConfigurationData" + arguments: '-TestType ConfigurationData -DependencyPath .\build\requiredModules.psd1' - task: PowerShell@2 name: buildpolicy displayName: Build policy XML inputs: filePath: '.\build\build.ps1' - arguments: "-IncludeRsop" + arguments: '-IncludeRsop -DependencyPath .\build\requiredModules.psd1' + - task: PublishBuildArtifacts@1 - displayName: "Publish Policy XML Files" + displayName: 'Publish Policy XML Files' inputs: - PathtoPublish: "output/Policies" + PathtoPublish: 'output/Policies' ArtifactName: Policies + - task: PublishBuildArtifacts@1 - displayName: "Publish Policy RSOP Files" + displayName: 'Publish Policy RSOP Files' inputs: - PathtoPublish: "output/Rsop" + PathtoPublish: 'output/Rsop' ArtifactName: Rsop - stage: publish dependsOn: build @@ -54,28 +57,29 @@ stages: runOnce: deploy: steps: - - download: None - - task: DownloadBuildArtifacts@0 - displayName: "Download Build Artifact: Rsop" - inputs: - buildType: "current" - artifactName: Rsop - downloadPath: $(Build.SourcesDirectory) - - task: DownloadBuildArtifacts@0 - displayName: "Download Build Artifact: Policies" - inputs: - buildType: "current" - artifactName: Policies - downloadPath: $(Build.SourcesDirectory) - - task: PowerShell@2 - name: publishpolicies - displayName: Publish policies - inputs: - filePath: '.\build\publish.ps1' - arguments: "-OutputPath $(Build.SourcesDirectory)" - - task: PowerShell@2 - name: validateintegration - displayName: Validate Integration - inputs: - filePath: '.\build\validate.ps1' - arguments: "-TestType Integration" + - download: None + + - task: DownloadBuildArtifacts@0 + displayName: 'Download Build Artifact: Rsop' + inputs: + buildType: 'current' + artifactName: Rsop + downloadPath: $(Build.SourcesDirectory) + - task: DownloadBuildArtifacts@0 + displayName: 'Download Build Artifact: Policies' + inputs: + buildType: 'current' + artifactName: Policies + downloadPath: $(Build.SourcesDirectory) + - task: PowerShell@2 + name: publishpolicies + displayName: Publish policies + inputs: + filePath: '.\build\publish.ps1' + arguments: '-OutputPath $(Build.SourcesDirectory) -DependencyPath .\build\requiredModules.psd1' + - task: PowerShell@2 + name: validateintegration + displayName: Validate Integration + inputs: + filePath: '.\build\validate.ps1' + arguments: '-TestType Integration -DependencyPath .\build\requiredModules.psd1' diff --git a/templates/AppLockerProject/build/build.ps1 b/templates/AppLockerProject/build/build.ps1 index 5a86cfb..5f3dc56 100644 --- a/templates/AppLockerProject/build/build.ps1 +++ b/templates/AppLockerProject/build/build.ps1 @@ -1,6 +1,9 @@ param ( + [string] + $DependencyPath = (Resolve-Path "$PSScriptRoot\requiredModules.psd1").Path, + [string] $SourcePath = "$PSScriptRoot\..\configurationdata", @@ -11,6 +14,12 @@ param $IncludeRsop ) +$psdependConfig = Import-PowerShellDataFile -Path $DependencyPath +$modPath = Resolve-Path -Path $psdependConfig.PSDependOptions.Target +$modOld = $env:PSModulePath +$pathSeparator = [System.IO.Path]::PathSeparator +$env:PSModulePath = "$modPath$pathSeparator$modOld" + $SourcePath = Resolve-Path -Path $SourcePath -ErrorAction Stop $OutputPath = if (-not (Resolve-Path -Path $OutputPath -ErrorAction SilentlyContinue)) { @@ -42,7 +51,11 @@ $datum = New-DatumStructure -DefinitionFile (Join-Path $SourcePath Datum.yml) $rsops = Get-DatumRsop $datum (Get-DatumNodesRecursive -AllDatumNodes $Datum.AllNodes) $rsops | Export-AlfXml -Path $policyPath -if (-not $IncludeRsop) { return } +if (-not $IncludeRsop) +{ + $env:PSModulePath = $modOld + return +} foreach ($rsop in $rsops) { @@ -53,3 +66,5 @@ foreach ($rsop in $rsops) } $rsop | ConvertTo-Yaml -OutFile (Join-Path -Path $domainPath -ChildPath "$($rsop.PolicyName).yml") -Force } + +$env:PSModulePath = $modOld diff --git a/templates/AppLockerProject/build/publish.ps1 b/templates/AppLockerProject/build/publish.ps1 index ae08da4..fa1f532 100644 --- a/templates/AppLockerProject/build/publish.ps1 +++ b/templates/AppLockerProject/build/publish.ps1 @@ -1,9 +1,18 @@ param ( + [string] + $DependencyPath = (Resolve-Path "$PSScriptRoot\requiredModules.psd1").Path, + [string] $OutputPath = (Resolve-Path "$PSScriptRoot\..\output").Path ) +$psdependConfig = Import-PowerShellDataFile -Path $DependencyPath +$modPath = Resolve-Path -Path $psdependConfig.PSDependOptions.Target +$modOld = $env:PSModulePath +$pathSeparator = [System.IO.Path]::PathSeparator +$env:PSModulePath = "$modPath$pathSeparator$modOld" + foreach ($policy in (Get-ChildItem -Path (Join-Path -Path $OutputPath -ChildPath Policies) -Recurse -Filter *.xml)) { $searcher = [adsisearcher]::new() @@ -19,3 +28,5 @@ foreach ($policy in (Get-ChildItem -Path (Join-Path -Path $OutputPath -ChildPath Set-AppLockerPolicy -XmlPolicy $policy.FullName -Ldap $policyFound.Path } + +$env:PSModulePath = $modOld diff --git a/templates/AppLockerProject/build/requiredModules.psd1 b/templates/AppLockerProject/build/requiredModules.psd1 index 4e0380e..42084ea 100644 --- a/templates/AppLockerProject/build/requiredModules.psd1 +++ b/templates/AppLockerProject/build/requiredModules.psd1 @@ -1,6 +1,6 @@ @{ PSDependOptions = @{ - AddToPath = $true + AddToPath = $false Target = 'output\RequiredModules' Parameters = @{ Repository = 'PSGallery' diff --git a/templates/AppLockerProject/build/validate.ps1 b/templates/AppLockerProject/build/validate.ps1 index 04c8ddd..5503c46 100644 --- a/templates/AppLockerProject/build/validate.ps1 +++ b/templates/AppLockerProject/build/validate.ps1 @@ -1,6 +1,9 @@ [CmdletBinding()] param ( + [string] + $DependencyPath = (Resolve-Path "$PSScriptRoot\requiredModules.psd1").Path, + [string] $ProjectRoot = (Resolve-Path "$PSScriptRoot\..").Path, @@ -9,7 +12,13 @@ param $TestType ) -Import-Module Pester +$psdependConfig = Import-PowerShellDataFile -Path $DependencyPath +$modPath = Resolve-Path -Path $psdependConfig.PSDependOptions.Target +$modOld = $env:PSModulePath +$pathSeparator = [System.IO.Path]::PathSeparator +$env:PSModulePath = "$modPath$pathSeparator$modOld" + +Import-Module Pester -Force -ErrorAction Stop -MinimumVersion 5.0.0 $global:testroot = Join-Path $ProjectRoot tests $po = [PesterConfiguration]::New() @@ -21,6 +30,8 @@ $po.TestResult.OutputPath = Join-Path $global:testroot 'testresults.xml' $po.TestResult.OutputFormat = 'NUnit2.5' $result = Invoke-Pester -Configuration $po +$env:PSModulePath = $modOld + if ($result.FailedCount -gt 0) { throw "Pester tests failed" } From dcfa87644ba2a3f1b7888e1ed7452e164c94c55a Mon Sep 17 00:00:00 2001 From: Jan-Hendrik Peters Date: Sat, 17 Jun 2023 12:02:40 +0200 Subject: [PATCH 09/11] Update pipeline to include tests --- templates/AppLockerProject/azurepipelines.yml | 154 +++++++++--------- 1 file changed, 80 insertions(+), 74 deletions(-) diff --git a/templates/AppLockerProject/azurepipelines.yml b/templates/AppLockerProject/azurepipelines.yml index c7add4e..3dc62a8 100644 --- a/templates/AppLockerProject/azurepipelines.yml +++ b/templates/AppLockerProject/azurepipelines.yml @@ -3,83 +3,89 @@ trigger: include: - main -stages: - - stage: build - jobs: - - job: Build - displayName: 'Build AppLocker Artifacts' - pool: - name: Default - workspace: - clean: all - steps: - - task: PowerShell@2 - name: prereq - displayName: 'Download prerequisites' +jobs: + - job: Build + displayName: 'Build AppLocker Artifacts' + pool: + name: Default + steps: + - task: PowerShell@2 + name: prereq + displayName: 'Download prerequisites' + inputs: + filePath: '.\build\prerequisites.ps1' + arguments: '-DependencyPath (Join-Path $(Build.SourcesDirectory) build\requiredModules.psd1)' + - task: PowerShell@2 + name: validateyaml + displayName: Validate Configuration Data + inputs: + filePath: '.\build\validate.ps1' + arguments: '-TestType ConfigurationData -DependencyPath (Join-Path $(Build.SourcesDirectory) build\requiredModules.psd1) -ProjectRoot $(Build.SourcesDirectory)' + - task: PowerShell@2 + name: buildpolicy + displayName: Build policy XML + inputs: + filePath: '.\build\build.ps1' + arguments: '-IncludeRsop -DependencyPath (Join-Path $(Build.SourcesDirectory) build\requiredModules.psd1) -SourcePath (Join-Path $(Build.SourcesDirectory) configurationdata) -OutputPath (Join-Path $(Build.SourcesDirectory) output)' + - task: PublishBuildArtifacts@1 + displayName: 'Publish Policy XML Files' + inputs: + PathtoPublish: 'output/Policies' + ArtifactName: Policies + - task: PublishBuildArtifacts@1 + displayName: 'Publish Policy RSOP Files' + inputs: + PathtoPublish: 'output/Rsop' + ArtifactName: Rsop + - task: PublishTestResults@2 + displayName: 'Publish Configuration Data Test Results' + condition: succeededOrFailed() + inputs: + testResultsFormat: 'NUnit' + testResultsFiles: 'tests/testresults.xml' + mergeTestResults: true + failTaskOnFailedTests: true + testRunTitle: 'Configuration Data Tests' + - deployment: Prod + dependsOn: Build + displayName: Prod Deployment + environment: Prod + pool: + name: Default + strategy: + runOnce: + deploy: + steps: + - task: DownloadBuildArtifacts@0 + displayName: 'Download Build Artifact: Rsop' inputs: - filePath: '.\build\prerequisites.ps1' - arguments: '-DependencyPath .\build\requiredModules.psd1' - - task: PowerShell@2 - name: validateyaml - displayName: Validate Configuration Data + buildType: 'current' + artifactName: Rsop + downloadPath: $(Build.SourcesDirectory) + - task: DownloadBuildArtifacts@0 + displayName: 'Download Build Artifact: Policies' inputs: - filePath: '.\build\validate.ps1' - arguments: '-TestType ConfigurationData -DependencyPath .\build\requiredModules.psd1' + buildType: 'current' + artifactName: Policies + downloadPath: $(Build.SourcesDirectory) - task: PowerShell@2 - name: buildpolicy - displayName: Build policy XML + name: publishpolicies + displayName: Publish policies inputs: - filePath: '.\build\build.ps1' - arguments: '-IncludeRsop -DependencyPath .\build\requiredModules.psd1' - - - task: PublishBuildArtifacts@1 - displayName: 'Publish Policy XML Files' + filePath: '.\build\publish.ps1' + arguments: '-DependencyPath (Join-Path $(Build.SourcesDirectory) build\requiredModules.psd1) -OutputPath (Join-Path $(Build.SourcesDirectory) output)' + - task: PowerShell@2 + name: validateintegration + displayName: Validate Integration inputs: - PathtoPublish: 'output/Policies' - ArtifactName: Policies - - - task: PublishBuildArtifacts@1 - displayName: 'Publish Policy RSOP Files' + filePath: '.\build\validate.ps1' + arguments: '-TestType Integration -DependencyPath (Join-Path $(Build.SourcesDirectory) build\requiredModules.psd1) -ProjectRoot $(Build.SourcesDirectory)' + - task: PublishTestResults@2 + displayName: 'Publish Integration Test Results' + condition: succeededOrFailed() inputs: - PathtoPublish: 'output/Rsop' - ArtifactName: Rsop - - stage: publish - dependsOn: build - jobs: - - deployment: Prod - displayName: Prod Deployment - environment: Prod - pool: - name: Default - workspace: - clean: all - strategy: - runOnce: - deploy: - steps: - - download: None - - - task: DownloadBuildArtifacts@0 - displayName: 'Download Build Artifact: Rsop' - inputs: - buildType: 'current' - artifactName: Rsop - downloadPath: $(Build.SourcesDirectory) - - task: DownloadBuildArtifacts@0 - displayName: 'Download Build Artifact: Policies' - inputs: - buildType: 'current' - artifactName: Policies - downloadPath: $(Build.SourcesDirectory) - - task: PowerShell@2 - name: publishpolicies - displayName: Publish policies - inputs: - filePath: '.\build\publish.ps1' - arguments: '-OutputPath $(Build.SourcesDirectory) -DependencyPath .\build\requiredModules.psd1' - - task: PowerShell@2 - name: validateintegration - displayName: Validate Integration - inputs: - filePath: '.\build\validate.ps1' - arguments: '-TestType Integration -DependencyPath .\build\requiredModules.psd1' + testResultsFormat: 'NUnit' + testResultsFiles: 'tests/testresults.xml' + mergeTestResults: true + failTaskOnFailedTests: true + testRunTitle: 'Integration Tests' From 4284d113dae47db6fa01e6fcf1f7f4bd36814db1 Mon Sep 17 00:00:00 2001 From: Jan-Hendrik Peters Date: Sun, 18 Jun 2023 18:20:09 +0200 Subject: [PATCH 10/11] Update pipeline templates --- .../.github/workflows/publish.yml | 14 +++++++------- .../.github/workflows/validate.yml | 10 +++++----- templates/AppLockerProject/build/publish.ps1 | 7 +++++++ .../\303\276domainfqdn\303\276/Pol1.yml" | 15 ++++++++++++--- .../configurationdata/readme.md | 17 ++++++++++++++++- 5 files changed, 47 insertions(+), 16 deletions(-) diff --git a/templates/AppLockerProject/.github/workflows/publish.yml b/templates/AppLockerProject/.github/workflows/publish.yml index f9919c0..e61adb2 100644 --- a/templates/AppLockerProject/.github/workflows/publish.yml +++ b/templates/AppLockerProject/.github/workflows/publish.yml @@ -12,13 +12,13 @@ jobs: steps: - uses: actions/checkout@v1 - name: Install Prerequisites - run: .\build\vsts-prerequisites.ps1 + run: .\build\prerequisites.ps1 -BuildWorker -DependencyPath (Join-Path $env:GITHUB_WORKSPACE build\requiredModules.psd1) shell: powershell - name: Validate Configuration Data - run: .\build\vsts-validate.ps1 -TestType ConfigurationData + run: .\build\validate.ps1 -TestType ConfigurationData -DependencyPath (Join-Path $env:GITHUB_WORKSPACE build\requiredModules.psd1) -ProjectRoot $env:GITHUB_WORKSPACE shell: powershell - name: Build - run: .\build\vsts-build.ps1 -IncludeRsop + run: .\build\build.ps1 -IncludeRsop -DependencyPath (Join-Path $env:GITHUB_WORKSPACE build\requiredModules.psd1) -SourcePath (Join-Path $env:GITHUB_WORKSPACE configurationdata) -OutputPath (Join-Path $env:GITHUB_WORKSPACE output) shell: powershell - uses: actions/upload-artifact@v3 with: @@ -26,9 +26,9 @@ jobs: path: | .\output\rsop .\output\policies - - name: Validate Integration Tests - run: .\build\vsts-validate.ps1 -TestType Integration - shell: powershell - name: Publish - run: .\build\vsts-publish.ps1 + run: .\build\publish.ps1 + shell: powershell + - name: Validate Integration Tests + run: .\build\validate.ps1 -TestType Integration -DependencyPath (Join-Path $env:GITHUB_WORKSPACE build\requiredModules.psd1) -ProjectRoot $env:GITHUB_WORKSPACE shell: powershell \ No newline at end of file diff --git a/templates/AppLockerProject/.github/workflows/validate.yml b/templates/AppLockerProject/.github/workflows/validate.yml index 1931439..b056ed6 100644 --- a/templates/AppLockerProject/.github/workflows/validate.yml +++ b/templates/AppLockerProject/.github/workflows/validate.yml @@ -8,17 +8,17 @@ jobs: steps: - uses: actions/checkout@v1 - name: Install Prerequisites - run: .\build\vsts-prerequisites.ps1 + run: .\build\prerequisites.ps1 -BuildWorker -DependencyPath (Join-Path $env:GITHUB_WORKSPACE build\requiredModules.psd1) shell: powershell - - name: Validate - run: .\build\vsts-validate.ps1 -TestType ConfigurationData + - name: Validate Configuration Data + run: .\build\validate.ps1 -TestType ConfigurationData -DependencyPath (Join-Path $env:GITHUB_WORKSPACE build\requiredModules.psd1) -ProjectRoot $env:GITHUB_WORKSPACE shell: powershell - name: Build - run: .\build\vsts-build.ps1 -IncludeRsop + run: .\build\build.ps1 -IncludeRsop -DependencyPath (Join-Path $env:GITHUB_WORKSPACE build\requiredModules.psd1) -SourcePath (Join-Path $env:GITHUB_WORKSPACE configurationdata) -OutputPath (Join-Path $env:GITHUB_WORKSPACE output) shell: powershell - uses: actions/upload-artifact@v3 with: name: build-artifacts path: | .\output\rsop - .\output\policies \ No newline at end of file + .\output\policies diff --git a/templates/AppLockerProject/build/publish.ps1 b/templates/AppLockerProject/build/publish.ps1 index fa1f532..a5a4ce8 100644 --- a/templates/AppLockerProject/build/publish.ps1 +++ b/templates/AppLockerProject/build/publish.ps1 @@ -12,6 +12,7 @@ $modPath = Resolve-Path -Path $psdependConfig.PSDependOptions.Target $modOld = $env:PSModulePath $pathSeparator = [System.IO.Path]::PathSeparator $env:PSModulePath = "$modPath$pathSeparator$modOld" +$rsops = Get-DatumRsopCache foreach ($policy in (Get-ChildItem -Path (Join-Path -Path $OutputPath -ChildPath Policies) -Recurse -Filter *.xml)) { @@ -24,6 +25,12 @@ foreach ($policy in (Get-ChildItem -Path (Join-Path -Path $OutputPath -ChildPath $null = New-GPO -Name $policy.BaseName -Comment "Auto-updated applocker policy" -Domain $policy.Directory.Name } + $rsop = $rsops | Where-Object { $_.Name -eq $policy.BaseName } + foreach ($link in $rsop.Links) + { + Set-GPLink -Name $rsop.PolicyName -Target $link.OrgUnitDn -LinkEnabled $link.Enabled -Enforced $link.Enforced -Order $link.Order -Domain $policy.Directory.Name -Confirm:0 + } + $policyFound = $searcher.FindOne() Set-AppLockerPolicy -XmlPolicy $policy.FullName -Ldap $policyFound.Path diff --git "a/templates/AppLockerProject/configurationdata/Policies/\303\276domainfqdn\303\276/Pol1.yml" "b/templates/AppLockerProject/configurationdata/Policies/\303\276domainfqdn\303\276/Pol1.yml" index c737ada..afe4b8d 100644 --- "a/templates/AppLockerProject/configurationdata/Policies/\303\276domainfqdn\303\276/Pol1.yml" +++ "b/templates/AppLockerProject/configurationdata/Policies/\303\276domainfqdn\303\276/Pol1.yml" @@ -1,5 +1,14 @@ -PolicyName: Pol1 -Domain: þdomainfqdnþ +PolicyName: "[x={ $Node.Name }=]" +Domain: "[x={ $File.Directory.BaseName } =]" +Links: + - OrgUnitDn: "OU=Prod,DC=contoso,DC=com" + LinkOrder: 1 + Enforced: yes # unspecified, no + Enabled: no # unspecified, yes + - OrgUnitDn: "OU=Test,DC=contoso,DC=com" + LinkOrder: 1 + Enforced: yes # unspecified, no + Enabled: yes # unspecified, no Apps: - Git - - Obs \ No newline at end of file + - Obs diff --git a/templates/AppLockerProject/configurationdata/readme.md b/templates/AppLockerProject/configurationdata/readme.md index 57ad53e..309441e 100644 --- a/templates/AppLockerProject/configurationdata/readme.md +++ b/templates/AppLockerProject/configurationdata/readme.md @@ -29,7 +29,22 @@ The idea is to describe content that is relevant for each domain. ## Policies Grouped by the domain, each policy should be a single yml file that contains -the Name, Domain and subscribed Apps for that policy. +the Name, Domain, optional GPLinks and subscribed Apps for that policy. + +Through the use of Datum.InvokeCommand, we can run scripts during the build +process, or rather: When generating the RSOP for the build. + +```yaml +PolicyName: "[x={ $Node.Name }=]" +Domain: "[x={ $File.Directory.BaseName } =]" +Links: + - OrgUnitDn: "OU=Prod,DC=contoso,DC=com" + LinkOrder: 1 + Enforced: yes # unspecified, no + Enabled: no # unspecified, yes +Apps: + - Git +``` ## Generics From d7b0abcd54ebc1f1edaa001a8ddf38ba6383ffa9 Mon Sep 17 00:00:00 2001 From: Jan-Hendrik Peters Date: Sun, 18 Jun 2023 18:46:55 +0200 Subject: [PATCH 11/11] Include GPLinks in publishing process --- templates/AppLockerProject/azurepipelines.yml | 2 +- templates/AppLockerProject/build/publish.ps1 | 32 ++++++++++++++++--- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/templates/AppLockerProject/azurepipelines.yml b/templates/AppLockerProject/azurepipelines.yml index 3dc62a8..363cb30 100644 --- a/templates/AppLockerProject/azurepipelines.yml +++ b/templates/AppLockerProject/azurepipelines.yml @@ -73,7 +73,7 @@ jobs: displayName: Publish policies inputs: filePath: '.\build\publish.ps1' - arguments: '-DependencyPath (Join-Path $(Build.SourcesDirectory) build\requiredModules.psd1) -OutputPath (Join-Path $(Build.SourcesDirectory) output)' + arguments: '-DependencyPath (Join-Path $(Build.SourcesDirectory) build\requiredModules.psd1) -SourcePath (Join-Path $(Build.SourcesDirectory) configurationdata) -OutputPath (Join-Path $(Build.SourcesDirectory) output)' - task: PowerShell@2 name: validateintegration displayName: Validate Integration diff --git a/templates/AppLockerProject/build/publish.ps1 b/templates/AppLockerProject/build/publish.ps1 index a5a4ce8..b9a39f0 100644 --- a/templates/AppLockerProject/build/publish.ps1 +++ b/templates/AppLockerProject/build/publish.ps1 @@ -4,7 +4,10 @@ param $DependencyPath = (Resolve-Path "$PSScriptRoot\requiredModules.psd1").Path, [string] - $OutputPath = (Resolve-Path "$PSScriptRoot\..\output").Path + $OutputPath = (Resolve-Path "$PSScriptRoot\..\output").Path, + + [string] + $SourcePath = "$PSScriptRoot\..\configurationdata" ) $psdependConfig = Import-PowerShellDataFile -Path $DependencyPath @@ -12,7 +15,8 @@ $modPath = Resolve-Path -Path $psdependConfig.PSDependOptions.Target $modOld = $env:PSModulePath $pathSeparator = [System.IO.Path]::PathSeparator $env:PSModulePath = "$modPath$pathSeparator$modOld" -$rsops = Get-DatumRsopCache +$datum = New-DatumStructure -DefinitionFile (Join-Path $SourcePath Datum.yml) +[hashtable[]] $rsops = Get-DatumRsop $datum (Get-DatumNodesRecursive -AllDatumNodes $Datum.AllNodes) foreach ($policy in (Get-ChildItem -Path (Join-Path -Path $OutputPath -ChildPath Policies) -Recurse -Filter *.xml)) { @@ -25,10 +29,30 @@ foreach ($policy in (Get-ChildItem -Path (Join-Path -Path $OutputPath -ChildPath $null = New-GPO -Name $policy.BaseName -Comment "Auto-updated applocker policy" -Domain $policy.Directory.Name } - $rsop = $rsops | Where-Object { $_.Name -eq $policy.BaseName } + $rsop = $rsops | Where-Object { $_['PolicyName'] -eq $policy.BaseName } foreach ($link in $rsop.Links) { - Set-GPLink -Name $rsop.PolicyName -Target $link.OrgUnitDn -LinkEnabled $link.Enabled -Enforced $link.Enforced -Order $link.Order -Domain $policy.Directory.Name -Confirm:0 + $param = @{ + Name = $rsop.PolicyName + Target = $link.OrgUnitDn + Domain = $policy.Directory.Name + Confirm = $false + } + + if ($rsop.ContainsKey('Enabled')) + { + $param['LinkEnabled'] = $link.Enabled + } + if ($rsop.ContainsKey('Enforced')) + { + $param['Enforced'] = $link.Enforced + } + if ($rsop.ContainsKey('Order')) + { + $param['Order'] = $link.Order + } + + Set-GPLink @param } $policyFound = $searcher.FindOne()