diff --git a/.build/GuestConfigurationTasks.ps1 b/.build/GuestConfigurationTasks.ps1 new file mode 100644 index 00000000..034c9490 --- /dev/null +++ b/.build/GuestConfigurationTasks.ps1 @@ -0,0 +1,249 @@ +param +( + [Parameter()] + [System.String] + $ProjectName = (property ProjectName ''), + + [Parameter()] + [System.String] + $SourcePath = (property SourcePath ''), + + [Parameter()] + [System.String] + $GCPackagesPath = (property GCPackagesPath 'GCPackages'), + + [Parameter()] + [System.String] + $GCPackagesOutputPath = (property GCPackagesOutputPath 'GCPackages'), + + [Parameter()] + [System.String] + $GCPoliciesPath = (property GCPoliciesPath 'GCPolicies'), + + [Parameter()] + [System.String] + $OutputDirectory = (property OutputDirectory (Join-Path $BuildRoot 'output')), + + [Parameter()] + [System.String] + $BuiltModuleSubdirectory = (property BuiltModuleSubdirectory ''), + + [Parameter()] + [System.String] + $BuildModuleOutput = (property BuildModuleOutput (Join-Path $OutputDirectory $BuiltModuleSubdirectory)), + + [Parameter()] + [System.String] + $ModuleVersion = (property ModuleVersion ''), + + [Parameter()] + [System.Collections.Hashtable] + $BuildInfo = (property BuildInfo @{ }) +) + +# SYNOPSIS: Building the Azure Policy Guest Configuration Packages +task build_guestconfiguration_packages_from_MOF -if ($PSVersionTable.PSEdition -eq 'Core') { + # Get the vales for task variables, see https://github.com/gaelcolas/Sampler#task-variables. + . Set-SamplerTaskVariable -AsNewBuild + + if (-not (Split-Path -IsAbsolute $GCPackagesPath)) + { + $GCPackagesPath = Join-Path -Path $SourcePath -ChildPath $GCPackagesPath + } + + if (-not (Split-Path -IsAbsolute $GCPoliciesPath)) + { + $GCPoliciesPath = Join-Path -Path $SourcePath -ChildPath $GCPoliciesPath + } + + "`tBuild Module Output = $BuildModuleOutput" + "`tGC Packages Path = $GCPackagesPath" + "`tGC Policies Path = $GCPoliciesPath" + "`t------------------------------------------------`r`n" + + $mofPath = Join-Path -Path $OutputDirectory -ChildPath $MofOutputFolder + $mofFiles = Get-ChildItem -Path $mofPath -Filter '*.mof' -Recurse | + Where-Object Name -NotLike ReferenceConfiguration* + + $moduleVersion = '2.0.0' + + foreach ($mofFile in $mofFiles) + { + $GCPackageName = $mofFile.BaseName + Write-Build DarkGray "Package Name '$GCPackageName' with Configuration '$MOFFile', OutputDirectory $OutputDirectory, GCPackagesOutputPath '$GCPackagesOutputPath'." + $GCPackageOutput = Get-SamplerAbsolutePath -Path $GCPackagesOutputPath -RelativeTo $OutputDirectory + + $NewGCPackageParams = @{ + Configuration = $mofFile.FullName + Name = $mofFile.BaseName + Path = $GCPackageOutput + Force = $true + Version = $ModuleVersion + Type = 'AuditAndSet' + } + + foreach ($paramName in (Get-Command -Name 'New-GuestConfigurationPackage' -ErrorAction Stop).Parameters.Keys.Where({ $_ -in $newPackageExtraParams.Keys })) + { + Write-Verbose -Message "`t Testing for parameter '$paramName'." + Write-Build DarkGray "`t`t Using configured parameter '$paramName' with value '$($newPackageExtraParams[$paramName])'." + # Override the Parameters from the $GCPackageName.psd1 + $NewGCPackageParams[$paramName] = $newPackageExtraParams[$paramName] + } + + $ZippedGCPackage = (& { + New-GuestConfigurationPackage @NewGCPackageParams + } 2>&1).Where{ + if ($_ -isnot [System.Management.Automation.ErrorRecord]) + { + # Filter out the Error records from New-GuestConfigurationPackage + $true + } + elseif ($_.Exception.Message -notmatch '^A second CIM class definition') + { + # Write non-terminating errors that are not "A second CIM class definition for .... was found..." + $false + Write-Error $_ -ErrorAction Continue + } + else + { + $false + } + } + + Write-Build DarkGray "`t Zips created, you may want to delete the unzipped folders under '$GCPackagesOutputPath'..." + + if ($ModuleVersion) + { + $GCPackageWithVersionZipName = ('{0}_{1}.zip' -f $GCPackageName, $ModuleVersion) + $GCPackageOutputPath = Get-SamplerAbsolutePath -Path $GCPackagesOutputPath -RelativeTo $OutputDirectory + $versionedGCPackageName = Join-Path -Path $GCPackageOutputPath -ChildPath $GCPackageWithVersionZipName + Write-Build DarkGray "`t Renaming Zip as '$versionedGCPackageName'." + $ZippedGCPackagePath = Move-Item -Path $ZippedGCPackage.Path -Destination $versionedGCPackageName -Force -PassThru + $ZippedGCPackage = @{ + Name = $ZippedGCPackage.Name + Path = $ZippedGCPackagePath.FullName + } + } + + Write-Build Green "`tZipped Guest Config Package: $($ZippedGCPackage.Path)" + } +} + +task publish_guestconfiguration_packages -if ( + $PSVersionTable.PSEdition -eq 'Core' -and $env:azureClientSecret +) { + $subscriptionId = $datum.Global.Azure.SubscriptionId + $tenantId = $datum.Global.Azure.TenantId + $resourceGroupName = $datum.Global.Azure.ResourceGroupName + $storageAccountName = $datum.Global.Azure.StorageAccountName + + Update-AzConfig -DisplayBreakingChangeWarning $false + + $credential = New-Object System.Management.Automation.PSCredential -ArgumentList $env:azureClientId, (ConvertTo-SecureString -String $env:azureClientSecret -AsPlainText -Force) + Connect-AzAccount -Credential $Credential -Tenant $tenantId -ServicePrincipal -SubscriptionId $subscriptionId | Out-Null + + $resourceGroup = Get-AzResourceGroup -Name $resourceGroupName + $guestConfigurationContainerName = 'guestconfiguration' + + if (-not (Get-AzStorageAccount -ResourceGroupName $resourceGroupName -Name $storageAccountName -ErrorAction SilentlyContinue)) + { + $param = @{ + ResourceGroupName = $resourceGroupName + Name = $storageAccountName + Location = $resourceGroup.Location + SkuName = 'Standard_LRS' + Kind = 'StorageV2' + } + + New-AzStorageAccount @param | Out-Null + } + + $storageAccountKeys = Get-AzStorageAccountKey -ResourceGroupName $resourceGroupName -Name $storageAccountName + $storageContext = New-AzStorageContext -StorageAccountName $storageAccountName -StorageAccountKey $storageAccountKeys[0].Value + if (-not (Get-AzStorageContainer -Context $storageContext -Name guestconfiguration -ErrorAction SilentlyContinue)) + { + New-AzStorageContainer -Context $storageContext -Name guestconfiguration -Permission Blob | Out-Null + } + + $moduleVersion = '2.0.0' + + $managedIdentity = Get-AzUserAssignedIdentity -ResourceGroupName $resourceGroupName -Name GCLab1_Remediation + + $gpPackages = Get-ChildItem -Path $OutputDirectory\$GCPackagesOutputPath -Filter '*.zip' -Recurse + foreach ($gpPackage in $gpPackages) + { + $policyName = $gpPackage.BaseName.Split('_')[0] + + $param = @{ + Container = $guestConfigurationContainerName + File = $gpPackage.FullName + Blob = $gpPackage.Name + Context = $storageContext + Force = $true + } + Set-AzStorageBlobContent @param + + $param = @{ + Context = $storageContext + FullUri = $true + Container = $guestConfigurationContainerName + Blob = $gpPackage.Name + Permission = 'rwd' + } + $contentUri = New-AzStorageBlobSASToken @param + + $params = @{ + PolicyId = New-Guid + ContentUri = $contentUri + DisplayName = $policyName + Description = 'none' + Path = "$OutputDirectory\$GCPoliciesPath" + Platform = 'Windows' + PolicyVersion = $moduleVersion + Mode = 'ApplyAndAutoCorrect' + Verbose = $true + } + + $policy = New-GuestConfigurationPolicy @params + + try + { + $policyDefinition = New-AzPolicyDefinition -Name $policyName -Policy $Policy.Path -ErrorAction Stop + } + catch + { + if ($_.Exception.HttpStatus -eq 'Forbidden') + { + Write-Error -Message "You do not have permission to create a Guest Configuration Policy. Please ensure you have the correct permissions, for example be a 'Resource Policy Contributor' on the Azure Subscription." -Exception $_.Exception + } + else + { + Write-Error -ErrorRecord $_ + } + continue + } + + $vm = Get-AzVM -Name $policyName -ResourceGroupName $resourceGroupName + + $param = @{ + Name = $policyName + DisplayName = $policyDefinition.Properties.DisplayName + Scope = $vm.Id + PolicyDefinition = $policyDefinition + Location = $datum.Global.Azure.LocationName + IdentityType = 'UserAssigned' + IdentityId = $managedIdentity.Id + } + $assignment = New-AzPolicyAssignment @param -WarningAction SilentlyContinue + + $param = @{ + Name = "$($policyName)Remediation" + PolicyAssignmentId = $assignment.PolicyAssignmentId + Scope = $vm.Id + ResourceDiscoveryMode = 'ReEvaluateCompliance' + } + Start-AzPolicyRemediation @param + + } + +} diff --git a/.gitignore b/.gitignore index f798e48d..6e359e08 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,5 @@ output/RequiredModules/* !output/RequiredModules/Sampler.DscPipeline !output/RequiredModules/ProtectedData +!output/RequiredModules/FileSystemDsc +!output/RequiredModules/DscConfig.Demo diff --git a/.work/Debug.ps1 b/.work/Debug.ps1 new file mode 100644 index 00000000..deabfe1a --- /dev/null +++ b/.work/Debug.ps1 @@ -0,0 +1,2 @@ +$env:azureClientSecret = 'ygR8Q~Uyy0QkiefumgWPd6NoNbyGwHCh4r6bFdf0' +$env:azureClientId = 'a0732820-7169-4ac5-bf37-755386f5e224' diff --git a/.work/cleanup.ps1 b/.work/cleanup.ps1 new file mode 100644 index 00000000..63a0ea7e --- /dev/null +++ b/.work/cleanup.ps1 @@ -0,0 +1,5 @@ +Get-AzVM -ResourceGroupName GCLab1 | ForEach-Object { + Get-AzPolicyAssignment -Scope $_.Id | Remove-AzPolicyAssignment +} + +Get-AzPolicyDefinition | Where-Object Name -like dsc* | Remove-AzPolicyDefinition -Force diff --git a/CHANGELOG.md b/CHANGELOG.md index a5c6e0c0..1e90ed43 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Added stages to cloud pipeline and added steps to publish modules to Azure Automation DSC. +- Added support for Azure Guest Configuration. ### Changed @@ -15,6 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Migration to Pester 5+. - Changed from 'CommonTasks' to 'DscConfig.Demo' for faster build time. - Added support for PowerShell 7. +- Added support for Azure Guest Configuration. ### Fixed diff --git a/Lab/10 Azure Guest Configuration Lab.ps1 b/Lab/10 Azure Guest Configuration Lab.ps1 new file mode 100644 index 00000000..c22f1bd1 --- /dev/null +++ b/Lab/10 Azure Guest Configuration Lab.ps1 @@ -0,0 +1,34 @@ +New-LabDefinition -Name GCLab1 -DefaultVirtualizationEngine Azure + +Add-LabAzureSubscription -SubscriptionName 'S1 Contoso2' -DefaultLocationName 'UK South' + +#Add-LabVirtualNetworkDefinition -Name $labName -AddressSpace 192.168.111.0/24 + +Add-LabDomainDefinition -Name contoso.com -AdminUser Install -AdminPassword Somepass1 +Set-LabInstallationCredential -Username Install -Password Somepass1 + +$PSDefaultParameterValues = @{ + 'Add-LabMachineDefinition:OperatingSystem' = 'Windows Server 2022 Datacenter (Desktop Experience)' + 'Add-LabMachineDefinition:AzureRoleSize' = 'Standard_D2ds_v4' + 'Add-LabMachineDefinition:DomainName' = 'contoso.com' +} + +$postInstallActivity = @() +$postInstallActivity += Get-LabPostInstallationActivity -ScriptFileName 'New-ADLabAccounts 2.0.ps1' -DependencyFolder $labSources\PostInstallationActivities\PrepareFirstChildDomain +$postInstallActivity += Get-LabPostInstallationActivity -ScriptFileName PrepareRootDomain.ps1 -DependencyFolder $labSources\PostInstallationActivities\PrepareRootDomain +Add-LabMachineDefinition -Name DSCDC01 -Roles RootDC -PostInstallationActivity $postInstallActivity + +Add-LabMachineDefinition -Name DSCFile01 -Roles FileServer +Add-LabMachineDefinition -Name DSCWeb01 -Roles WebServer + +Add-LabMachineDefinition -Name DSCFile02 -Roles FileServer +Add-LabMachineDefinition -Name DSCWeb02 -Roles WebServer + +Add-LabMachineDefinition -Name DSCFile03 -Roles FileServer +Add-LabMachineDefinition -Name DSCWeb03 -Roles WebServer + +Install-Lab + +Checkpoint-LabVM -All -SnapshotName AfterInstall + +Show-LabDeploymentSummary -Detailed diff --git a/Lab/20 Azure Guest Configuration Lab Customizations.ps1 b/Lab/20 Azure Guest Configuration Lab Customizations.ps1 new file mode 100644 index 00000000..63dce3fe --- /dev/null +++ b/Lab/20 Azure Guest Configuration Lab Customizations.ps1 @@ -0,0 +1,42 @@ +#Requires -Modules AutomatedLab, Az.Resources, Az.ManagedServiceIdentity + +if (-not $lab) +{ + $lab = Import-Lab -Name GCLab1 -NoValidation -PassThru +} + +Get-LabVM | ForEach-Object { + + Write-Host "Assigning system assigned identity to virtual machine '$($_.Name)'" + $vm = Get-AzVM -Name $_.Name -ResourceGroupName $lab.AzureSettings.DefaultResourceGroup.ResourceGroupName + Update-AzVM -VM $vm -IdentityType SystemAssigned -ResourceGroupName $vm.ResourceGroupName | Out-Null + + Write-Host "Installing Guest Configuration extension on virtual machine '$($_.Name)'" + $param = @{ + Publisher = 'Microsoft.GuestConfiguration' + ExtensionType = 'ConfigurationforWindows' + Name = 'AzurePolicyforWindows' + TypeHandlerVersion = '1.0' + ResourceGroupName = $vm.ResourceGroupName + Location = $vm.Location + VMName = $vm.Name + EnableAutomaticUpgrade = $true + } + Set-AzVMExtension @param | Out-Null +} + +Write-Host "Creating user assigned identity for remediation '$($lab.Name)_Remediation'" +New-AzUserAssignedIdentity -ResourceGroupName $lab.AzureSettings.DefaultResourceGroup -Name "$($lab.Name)_Remediation" -Location $lab.AzureSettings.DefaultLocation +Start-Sleep -Seconds 10 +$managedIdentity = Get-AzADServicePrincipal -DisplayName "$($lab.Name)_Remediation" + +Write-Host "Assigning role 'Contributor' to user assigned identity '$($managedIdentity.DisplayName)'" +New-AzRoleAssignment -ObjectId $managedIdentity.Id -RoleDefinitionName Contributor -Scope $lab.AzureSettings.DefaultResourceGroup.ResourceId +Write-Host "Assigning role 'Guest Configuration Resource Contributor' to user assigned identity '$($managedIdentity.DisplayName)'" +New-AzRoleAssignment -ObjectId $managedIdentity.Id -RoleDefinitionName 'Guest Configuration Resource Contributor' -Scope $lab.AzureSettings.DefaultResourceGroup.ResourceId + +#DevOps Organization and Service Connection must be created prior to running these lines +$azureDevOpsOrganizationName = 'randree' +$azureDevOpsProjectName = 'DscWorkshop' +$serviceConnectionServicePrincipal = Get-AzADServicePrincipal | Where-Object DisplayName -like "$azureDevOpsOrganizationName-$azureDevOpsProjectName*" +New-AzRoleAssignment -ObjectId $serviceConnectionServicePrincipal.Id -RoleDefinitionName 'Resource Policy Contributor' diff --git a/RequiredModules.psd1 b/RequiredModules.psd1 index add6a769..c9b55712 100644 --- a/RequiredModules.psd1 +++ b/RequiredModules.psd1 @@ -34,6 +34,14 @@ 'DscResource.Test' = '0.16.1' 'DscResource.DocGenerator' = '0.11.2' PSDesiredStateConfiguration = '2.0.6' + GuestConfiguration = '4.4.0' + + 'Az.Accounts' = '2.12.3' + 'Az.Storage' = '5.7.0' + 'Az.ManagedServiceIdentity' = '1.1.1' + 'Az.Resources' = '6.7.0' + 'Az.PolicyInsights' = '1.6.1' + 'Az.Compute' = '6.0.0' # Composites 'DscConfig.Demo' = '0.8.3' @@ -44,7 +52,7 @@ NetworkingDsc = '9.0.0' JeaDsc = '4.0.0-preview0005' WebAdministrationDsc = '4.1.0' - FileSystemDsc = '1.1.1' + #FileSystemDsc = '1.1.1' SecurityPolicyDsc = '2.10.0.0' xDscDiagnostics = '2.8.0' diff --git a/Resolve-Dependency.psd1 b/Resolve-Dependency.psd1 index 7db04d81..b938f9a5 100644 --- a/Resolve-Dependency.psd1 +++ b/Resolve-Dependency.psd1 @@ -1,30 +1,5 @@ @{ - #PSDependTarget = './output/modules' - #Proxy = '' - #ProxyCredential = '$MyCredentialVariable' #TODO: find a way to support credentials in build (resolve variable) - Gallery = 'PSGallery' - - # To use a private nuget repository change the following to your own feed. The locations must be a Nuget v2 feed due - # to limitation in PowerShellGet v2.x. Example below is for a Azure DevOps Server project-scoped feed. While resolving - # dependencies it will be registered as a trusted repository with the name specified in the property 'Gallery' above, - # unless property 'Name' is provided in the hashtable below, if so it will override the property 'Gallery' above. The - # registered repository will be removed when dependencies has been resolved, unless it was already registered to begin - # with. If repository is registered already but with different URL:s the repository will be re-registered and reverted - # after dependencies has been resolved. Currently only Windows integrated security works with private Nuget v2 feeds - # (or if it is a public feed with no security), it is not possible yet to securely provide other credentials for the feed. - #RegisterGallery = @{ - # #Name = 'MyPrivateFeedName' - # GallerySourceLocation = 'https://azdoserver.company.local///_packaging//nuget/v2' - # GalleryPublishLocation = 'https://azdoserver.company.local///_packaging//nuget/v2' - # GalleryScriptSourceLocation = 'https://azdoserver.company.local///_packaging//nuget/v2' - # GalleryScriptPublishLocation = 'https://azdoserver.company.local///_packaging//nuget/v2' - # #InstallationPolicy = 'Trusted' - #} - - #AllowOldPowerShellGetModule = $true - #MinimumPSDependVersion = '0.3.0' AllowPrerelease = $false WithYAML = $true # Will also bootstrap PowerShell-Yaml to read other config files } - diff --git a/azure-pipelines Guest Configuration.yml b/azure-pipelines Guest Configuration.yml new file mode 100644 index 00000000..d647419d --- /dev/null +++ b/azure-pipelines Guest Configuration.yml @@ -0,0 +1,126 @@ +trigger: + branches: + include: + - '*' + paths: + exclude: + - CHANGELOG.md + tags: + include: + - "v*" + exclude: + - "*-*" + +variables: + buildFolderName: output + testResultFolderName: testResults + defaultBranch: main + Agent.Source.Git.ShallowFetchDepth: 0 + +stages: + - stage: Build + jobs: + + - job: CompileDscOnPowerShellCore + displayName: Compile DSC Configuration on PowerShell Core + pool: + vmImage: 'windows-latest' + steps: + + - pwsh: | + dir -Path env: | Out-String | Write-Host + displayName: 'Display Environment Variables' + + - pwsh: | + dotnet tool install --global GitVersion.Tool + $gitVersionObject = dotnet-gitversion | ConvertFrom-Json + $gitVersionObject.PSObject.Properties.ForEach{ + Write-Host -Object "Setting Task Variable '$($_.Name)' with value '$($_.Value)'." + Write-Host -Object "##vso[task.setvariable variable=$($_.Name);]$($_.Value)" + } + Write-Host -Object "##vso[build.updatebuildnumber]$($gitVersionObject.FullSemVer)" + displayName: Calculate ModuleVersion (GitVersion) + + - task: PowerShell@2 + name: build + displayName: 'Build DSC Artifacts' + inputs: + filePath: './build.ps1' + arguments: '-ResolveDependency -tasks build' + pwsh: true + env: + ModuleVersion: $(NuGetVersionV2) + + - task: AzureCLI@2 + name: setVariables + displayName: Set Output Variables + continueOnError: false + inputs: + azureSubscription: GC1 + scriptType: ps + scriptLocation: inlineScript + addSpnToEnvironment: true + inlineScript: | + Write-Host "##vso[task.setvariable variable=azureClientId;isOutput=true]$($env:servicePrincipalId)" + Write-Host "##vso[task.setvariable variable=azureClientSecret;isOutput=true]$($env:servicePrincipalKey)" + Write-Host "##vso[task.setvariable variable=azureTenantId;isOutput=true]$($env:tenantId)" + + - task: PowerShell@2 + name: pack + displayName: 'Pack DSC Artifacts' + inputs: + filePath: './build.ps1' + arguments: '-tasks pack' + pwsh: true + env: + azureClientId: $(setVariables.azureClientId) + azureClientSecret: $(setVariables.azureClientSecret) + azureTenantId: $(setVariables.azureTenantId) + + - task: PublishPipelineArtifact@1 + displayName: 'Publish Output Folder' + inputs: + targetPath: '$(buildFolderName)/' + artifact: 'output7' + publishLocation: 'pipeline' + parallel: true + + - task: PublishPipelineArtifact@1 + displayName: 'Publish MOF Files' + inputs: + targetPath: '$(buildFolderName)/MOF' + artifact: 'MOF7' + publishLocation: 'pipeline' + parallel: true + + - task: PublishPipelineArtifact@1 + displayName: 'Publish Meta MOF Files' + inputs: + targetPath: '$(buildFolderName)/MetaMOF' + artifact: 'MetaMOF7' + publishLocation: 'pipeline' + parallel: true + + - task: PublishPipelineArtifact@1 + displayName: 'Publish Compressed Modules' + inputs: + targetPath: '$(buildFolderName)/CompressedModules' + artifact: 'CompressedModules7' + publishLocation: 'pipeline' + parallel: true + + - task: PublishPipelineArtifact@1 + displayName: 'Publish RSOP Files' + inputs: + targetPath: '$(buildFolderName)/RSOP' + artifact: 'RSOP7' + publishLocation: 'pipeline' + parallel: true + + - task: PublishPipelineArtifact@1 + displayName: 'Publish GC Packages' + inputs: + targetPath: '$(buildFolderName)/GCPackages' + artifact: 'GCPackages' + publishLocation: 'pipeline' + parallel: true diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 2bcbe974..c5f13587 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -127,12 +127,31 @@ stages: env: ModuleVersion: $(NuGetVersionV2) + - task: AzureCLI@2 + name: setVariables + displayName: Set Output Variables + continueOnError: false + inputs: + azureSubscription: GC1 + scriptType: ps + scriptLocation: inlineScript + addSpnToEnvironment: true + inlineScript: | + Write-Host "##vso[task.setvariable variable=azureClientId;isOutput=true]$($env:servicePrincipalId)" + Write-Host "##vso[task.setvariable variable=azureClientSecret;isOutput=true]$($env:servicePrincipalKey)" + Write-Host "##vso[task.setvariable variable=azureTenantId;isOutput=true]$($env:tenantId)" + - task: PowerShell@2 name: pack displayName: 'Pack DSC Artifacts' inputs: filePath: './build.ps1' arguments: '-tasks pack' + pwsh: true + env: + azureClientId: $(setVariables.azureClientId) + azureClientSecret: $(setVariables.azureClientSecret) + azureTenantId: $(setVariables.azureTenantId) - task: PublishPipelineArtifact@1 displayName: 'Publish Output Folder' @@ -173,3 +192,11 @@ stages: artifact: 'RSOP7' publishLocation: 'pipeline' parallel: true + + - task: PublishPipelineArtifact@1 + displayName: 'Publish GC Packages' + inputs: + targetPath: '$(buildFolderName)/GCPackages' + artifact: 'GCPackages' + publishLocation: 'pipeline' + parallel: true diff --git a/build.yaml b/build.yaml index 119aa1b7..85486709 100644 --- a/build.yaml +++ b/build.yaml @@ -13,7 +13,6 @@ BuildWorkflow: build: - Clean - PowerShell5Compatibility - #- build_guestconfiguration_packages_from_MOF - Build_Module_ModuleBuilder - LoadDatumConfigData - TestConfigData @@ -31,6 +30,8 @@ BuildWorkflow: - CompressModulesWithChecksum - Compress_Artifact_Collections - TestBuildAcceptance + - build_guestconfiguration_packages_from_MOF + - publish_guestconfiguration_packages rsop: - LoadDatumConfigData diff --git a/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/ComputerSettings/ComputerSettings.psd1 b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/ComputerSettings/ComputerSettings.psd1 new file mode 100644 index 00000000..814ea45a --- /dev/null +++ b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/ComputerSettings/ComputerSettings.psd1 @@ -0,0 +1,15 @@ +@{ + RootModule = 'ComputerSettings.schema.psm1' + + ModuleVersion = '0.0.1' + + GUID = '78f1dfd9-9108-4ccb-85d7-41ae370a6e68' + + Author = 'NA' + + CompanyName = 'NA' + + Copyright = 'NA' + + DscResourcesToExport = @('ComputerSettings') +} diff --git a/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/ComputerSettings/ComputerSettings.schema.psm1 b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/ComputerSettings/ComputerSettings.schema.psm1 new file mode 100644 index 00000000..c314a05f --- /dev/null +++ b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/ComputerSettings/ComputerSettings.schema.psm1 @@ -0,0 +1,78 @@ +configuration ComputerSettings { + param ( + [Parameter(Mandatory = $true)] + [string] + $Name, + + [Parameter()] + [string] + $Description, + + [Parameter()] + [string] + $DomainName, + + [Parameter()] + [string] + $WorkGroupName, + + [Parameter()] + [string] + $JoinOU, + + [Parameter()] + [pscredential] + $Credential, + + [Parameter()] + [string] + $TimeZone, + + [Parameter()] + [bool]$AllowRemoteDesktop, + + [Parameter()] + [ValidateSet('Secure', 'NonSecure')] + [string]$RemoteDesktopUserAuthentication + ) + + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName ComputerManagementDsc + + $timeZoneParamList = 'IsSingleInstance', 'TimeZone', 'PsDscRunAsCredential' + $computerParamList = 'Name', 'Credential', 'Description', 'DomainName', 'JoinOU', 'PsDscRunAsCredential', 'Server', 'UnjoinCredential', 'WorkGroupName' + + $params = @{ } + foreach ($item in ($PSBoundParameters.GetEnumerator() | Where-Object Key -In $computerParamList)) + { + $params.Add($item.Key, $item.Value) + } + (Get-DscSplattedResource -ResourceName Computer -ExecutionName "Computer$($params.Name)" -Properties $params -NoInvoke).Invoke($params) + + if ($TimeZone) + { + $params = @{ } + foreach ($item in ($PSBoundParameters.GetEnumerator() | Where-Object Key -In $timeZoneParamList)) + { + $params.Add($item.Key, $item.Value) + } + $params.Add('IsSingleInstance', 'Yes') + (Get-DscSplattedResource -ResourceName TimeZone -ExecutionName "TimeZone$($params.Name)" -Properties $params -NoInvoke).Invoke($params) + } + + if ($RemoteDesktopUserAuthentication) + { + $params = @{ } + $params.IsSingleInstance = 'Yes' + $params.UserAuthentication = $RemoteDesktopUserAuthentication + if ($AllowRemoteDesktop) + { + $params.Ensure = 'Present' + } + else + { + $params.Ensure = 'Absent' + } + (Get-DscSplattedResource -ResourceName RemoteDesktopAdmin -ExecutionName "RemoteDesktopAdmin$($params.Name)" -Properties $params -NoInvoke).Invoke($params) + } +} diff --git a/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/ConfigurationBase/ConfigurationBase.psd1 b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/ConfigurationBase/ConfigurationBase.psd1 new file mode 100644 index 00000000..1dbc8d30 --- /dev/null +++ b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/ConfigurationBase/ConfigurationBase.psd1 @@ -0,0 +1,15 @@ +@{ + RootModule = 'ConfigurationBase.schema.psm1' + + ModuleVersion = '0.0.1' + + GUID = 'e0d32fb8-6305-4fa3-835e-e89cca22aeba' + + Author = 'NA' + + CompanyName = 'NA' + + Copyright = 'NA' + + DscResourcesToExport = @('ConfigurationBase') +} diff --git a/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/ConfigurationBase/ConfigurationBase.schema.psm1 b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/ConfigurationBase/ConfigurationBase.schema.psm1 new file mode 100644 index 00000000..d486273f --- /dev/null +++ b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/ConfigurationBase/ConfigurationBase.schema.psm1 @@ -0,0 +1,19 @@ +configuration ConfigurationBase { + param ( + [Parameter()] + [ValidateSet('Baseline', 'WebServer', 'FileServer')] + [string]$SystemType + ) + + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName xPSDesiredStateConfiguration + + xRegistry EnableRdp + { + Key = 'HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Terminal Server' + ValueName = 'fDenyTSConnection' + ValueData = 0 + ValueType = 'Dword' + Ensure = 'Present' + } +} diff --git a/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/DscDiagnostic/DscDiagnostic.psd1 b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/DscDiagnostic/DscDiagnostic.psd1 new file mode 100644 index 00000000..7382e4bb --- /dev/null +++ b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/DscDiagnostic/DscDiagnostic.psd1 @@ -0,0 +1,15 @@ +@{ + RootModule = 'DscDiagnostic.schema.psm1' + + ModuleVersion = '0.0.1' + + GUID = '20392fe9-270d-4295-af2c-0749dd015596' + + Author = 'NA' + + CompanyName = 'NA' + + Copyright = 'NA' + + DscResourcesToExport = @('DscDiagnostic') +} diff --git a/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/DscDiagnostic/DscDiagnostic.schema.psm1 b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/DscDiagnostic/DscDiagnostic.schema.psm1 new file mode 100644 index 00000000..d2b1ac34 --- /dev/null +++ b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/DscDiagnostic/DscDiagnostic.schema.psm1 @@ -0,0 +1,201 @@ +function Stop-DscLocalConfigurationManager +{ + #usually the process consuming the most memory is the DSC LCM + $p = Get-Process -Name WmiPrvSE -IncludeUserName | + Where-Object UserName -eq 'NT AUTHORITY\SYSTEM' | + Sort-Object -Property WS -Descending | + Select-Object -First 1 + + Write-Verbose "The LCM is running un process with $($p.Id) consuming $([System.Math]::Round($p.WS / 1MB, 2))MB of memory (Working Set)." + $p | Stop-Process -Force + Write-Verbose 'The LCM process was killed' +} + +function Get-DscConfigurationVersion +{ + $hash = @{} + $key = Get-Item HKLM:\SOFTWARE\DscTagging -ErrorAction SilentlyContinue + + if ( $null -ne $key ) + { + foreach ($property in $key.Property) + { + $hash.Add($property, $key.GetValue($property)) + } + } + else + { + $hash.Version = 'Unknown' + } + + New-Object -TypeName PSObject -Property $hash +} + + +function Get-DscLcmControllerSettings +{ + $key = Get-Item HKLM:\SOFTWARE\DscLcmController + $hash = @{ } + foreach ($property in $key.Property) + { + $hash.Add($property, $key.GetValue($property)) + } + + $maintenanceWindows = Get-ChildItem -Path HKLM:\SOFTWARE\DscLcmController\MaintenanceWindows + $maintenanceWindowsHash = @{ } + foreach ($maintenanceWindow in $maintenanceWindows) + { + $mwHash = @{ } + foreach ($property in $maintenanceWindow.Property) + { + $mwHash.Add($property, $maintenanceWindow.GetValue($property)) + } + $maintenanceWindowsHash.Add($maintenanceWindow.PSChildName, $mwHash) + } + + $hash.Add('MaintenanceWindows', (New-Object -TypeName PSObject -Property $maintenanceWindowsHash)) + + New-Object -TypeName PSObject -Property $hash +} + +function Test-DscConfiguration +{ + PSDesiredStateConfiguration\Test-DscConfiguration -Detailed -Verbose +} +function Update-DscConfiguration +{ + PSDesiredStateConfiguration\Update-DscConfiguration -Wait -Verbose +} + +function Get-DscLocalConfigurationManager +{ + PSDesiredStateConfiguration\Get-DscLocalConfigurationManager +} +function Get-DscLcmControllerLog +{ + param ( + [Parameter()] + [switch]$AutoCorrect, + + [Parameter()] + [switch]$Refresh, + + [Parameter()] + [switch]$Monitor, + + [Parameter()] + [int]$Last = 1000 + ) + + Import-Csv -Path C:\ProgramData\Dsc\LcmController\LcmControllerSummary.csv | Where-Object { + if ($AutoCorrect) + { + [bool][int]$_.DoAutoCorrect -eq $AutoCorrect + } + else + { + $true + } + } | Where-Object { + if ($Refresh) + { + [bool][int]$_.DoRefresh -eq $Refresh + } + else + { + $true + } + } | Where-Object { + if ($Monitor) + { + [bool][int]$_.DoMonitor -eq $Monitor + } + else + { + $true + } + } | Microsoft.PowerShell.Utility\Select-Object -Last $Last +} + +function Start-DscConfiguration +{ + PSDesiredStateConfiguration\Start-DscConfiguration -UseExisting -Wait -Verbose +} + +function Get-DscOperationalEventLog +{ + Get-WinEvent -LogName "Microsoft-Windows-Dsc/Operational" +} + +function Get-DscTraceInformation +{ + param ( + [Parameter()] + [int]$Last = 100 + ) + + if (-not (Get-Module -ListAvailable -Name xDscDiagnostics)) + { + Write-Error "This function required the module 'xDscDiagnostics' to be present on the system" + return + } + + $failedJobs = Get-xDscOperation -Newest $Last | Where-Object Result -eq 'Failure' + + foreach ($failedJob in $failedJobs) + { + if ($failedJob.JobID) + { + Trace-xDscOperation -JobId $failedJob.JobID + } + else + { + Trace-xDscOperation -SequenceID $failedJob.SequenceId + } + } +} + +#------------------------------------------------------------------------------------------- + +Configuration DscDiagnostic { + + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName JeaDsc + + $visibleFunctions = 'Test-DscConfiguration', + 'Get-DscConfigurationVersion', + 'Update-DscConfiguration', + 'Get-DscLcmControllerLog', + 'Start-DscConfiguration', + 'Get-DscOperationalEventLog', + 'Get-DscTraceInformation', + 'Get-DscLcmControllerSettings', + 'Get-DscLocalConfigurationManager', + 'Stop-DscLocalConfigurationManager' + + $functionDefinitions = @() + foreach ($visibleFunction in $visibleFunctions) + { + $functionDefinitions += @{ + Name = $visibleFunction + ScriptBlock = (Get-Command -Name $visibleFunction).ScriptBlock + } | ConvertTo-Expression + } + + JeaRoleCapabilities ReadDiagnosticRole + { + Path = 'C:\Program Files\WindowsPowerShell\Modules\DscDiagnostics\RoleCapabilities\ReadDiagnosticsRole.psrc' + VisibleFunctions = $visibleFunctions + FunctionDefinitions = $functionDefinitions + } + + JeaSessionConfiguration DscEndpoint + { + Ensure = 'Present' + DependsOn = '[JeaRoleCapabilities]ReadDiagnosticRole' + Name = 'DSC' + RoleDefinitions = '@{ Everyone = @{ RoleCapabilities = "ReadDiagnosticsRole" } }' + SessionType = 'RestrictedRemoteServer' + ModulesToImport = 'PSDesiredStateConfiguration', 'xDscDiagnostics' + } +} diff --git a/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/DscLcmController/DscLcmController.psd1 b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/DscLcmController/DscLcmController.psd1 new file mode 100644 index 00000000..683cd2b5 --- /dev/null +++ b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/DscLcmController/DscLcmController.psd1 @@ -0,0 +1,15 @@ +@{ + RootModule = 'DscLcmController.schema.psm1' + + ModuleVersion = '0.0.1' + + GUID = '3ee0348a-223e-4f2b-b2f2-662fabccf05b' + + Author = 'NA' + + CompanyName = 'NA' + + Copyright = 'NA' + + DscResourcesToExport = @('DscLcmController') +} diff --git a/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/DscLcmController/DscLcmController.schema.psm1 b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/DscLcmController/DscLcmController.schema.psm1 new file mode 100644 index 00000000..f0ee731e --- /dev/null +++ b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/DscLcmController/DscLcmController.schema.psm1 @@ -0,0 +1,817 @@ +$dscLcmControllerScript = @' +function Get-DscConfigurationVersionLocal { + + $hash = @{} + $key = Get-Item HKLM:\SOFTWARE\DscTagging -ErrorAction SilentlyContinue + + if( $null -ne $key ) { + foreach ($property in $key.Property) { + $hash.Add($property, $key.GetValue($property)) + } + } + else { + $hash.Version = 'Unknown' + } + + New-Object -TypeName PSObject -Property $hash +} + +function Send-DscTaggingData { + [CmdletBinding()] + param() + + $pattern = 'http[s]?:\/\/(?([^\/:\.[:space:]]+(\.[^\/:\.[:space:]]+)*)|([0-9](\.[0-9]{3})))(:[0-9]+)?((\/[^?#[:space:]]+)(\?[^#[:space:]]+)?(\#.+)?)?' + try { + $lcm = Get-DscLocalConfigurationManager -ErrorAction Stop + $pullServerUrl = $lcm.ConfigurationDownloadManagers.ServerURL + $agentId = $lcm.AgentId + + Write-Host "PullServerUrl = '$pullServerUrl'" + Write-Host "AgentId = '$agentId'" + + $found = $pullServerUrl -match $pattern + + if (-not $found) { + Write-Error "Could not find pull server in Url '$pullServerUrl'" -ErrorAction Stop + } + } + catch { + Write-Error "Cannot get pull server name from 'Get-DscLocalConfigurationManager' output, the error was $($_.Exception.Message)" + return + } + + $versionData = Get-DscConfigurationVersionLocal + + if ($versionData.Layers.Count -gt 1) { + $versionData.Layers = $versionData.Layers -join ', ' + } + + Write-Host + Write-Host "Sending the following DSC version data to JEA endpoint on pull server '$($Matches.PullServer)'" + $versionData | Out-String | Write-Host + + Invoke-Command -ComputerName $Matches.PullServer -ConfigurationName DscData -ScriptBlock { + Send-DscTaggingData -AgentId $args[0] -Data $args[1] + } -ArgumentList $agentId, $versionData +} + +function Set-LcmPostpone { + $postponeInterval = 14 + if ($lastLcmPostpone.AddDays($postponeInterval) -gt (Get-Date)) { + Write-Host "Last LCM postpone was done at '$lastLcmPostpone'. Next one will not be triggered before '$($lastLcmPostpone.AddDays($postponeInterval))'" + Write-Host + return + } + else { + Write-Host "Last LCM postpone was done at '$lastLcmPostpone'. Triggering LCM postone as the last time was more than $postponeInterval ago" + Write-Host + } + + $currentLcmSettings = Get-DscLocalConfigurationManager + $maxConsistencyCheckInterval = if ($currentLcmSettings.ConfigurationModeFrequencyMins -eq 44640) { + 44639 #value must be changed in order to reset the LCM timer + } + else { + 44640 #minutes for 31 days + } + + $maxRefreshInterval = if ($currentLcmSettings.RefreshFrequencyMins -eq 44640) { + 44639 #value must be changed in order to reset the LCM timer + } + else { + 44640 #minutes for 31 days + } + + $metaMofFolder = mkdir -Path "$path\MetaMof" -Force + + if (Test-Path -Path C:\Windows\System32\Configuration\MetaConfig.mof) { + $mofFile = Copy-Item -Path C:\Windows\System32\Configuration\MetaConfig.mof -Destination "$path\MetaMof\localhost.meta.mof" -Force -PassThru + } + else { + $mofFile = Get-Item -Path "$path\MetaMof\localhost.meta.mof" -ErrorAction Stop + } + $content = Get-Content -Path $mofFile.FullName -Raw -Encoding Unicode + + $pattern = '(ConfigurationModeFrequencyMins(\s+)?=(\s+)?)(\d+)(;)' + $content = $content -replace $pattern, ('$1 {0}$5' -f $maxConsistencyCheckInterval) + + $pattern = '(RefreshFrequencyMins(\s+)?=(\s+)?)(\d+)(;)' + $content = $content -replace $pattern, ('$1 {0}$5' -f $maxRefreshInterval) + + $content | Out-File -FilePath $mofFile.FullName -Encoding unicode + + Set-DscLocalConfigurationManager -Path $metaMofFolder + + "$(Get-Date) - Postponed LCM" | Add-Content -Path "$path\LcmPostponeSummary.log" + + Set-ItemProperty -Path $dscLcmController.PSPath -Name LastLcmPostpone -Value (Get-Date) -Type String -Force +} + +function Test-InMaintenanceWindow { + if ($maintenanceWindows) { + $inMaintenanceWindow = foreach ($maintenanceWindow in $maintenanceWindows) { + Write-Host "Reading maintenance window '$($maintenanceWindow.PSChildName)'" + [datetime]$startTime = Get-ItemPropertyValue -Path $maintenanceWindow.PSPath -Name StartTime + [timespan]$timespan = Get-ItemPropertyValue -Path $maintenanceWindow.PSPath -Name Timespan + [datetime]$endTime = $startTime + $timespan + [string]$dayOfWeek = try { + Get-ItemPropertyValue -Path $maintenanceWindow.PSPath -Name DayOfWeek + } + catch { } + [string]$on = try { + Get-ItemPropertyValue -Path $maintenanceWindow.PSPath -Name On + } + catch { } + + if ($dayOfWeek) { + if ((Get-Date).DayOfWeek -ne $dayOfWeek) { + Write-Host "DayOfWeek is set to '$dayOfWeek'. Current day of week is '$((Get-Date).DayOfWeek)', maintenance window does not apply" + continue + } + else { + Write-Host "Maintenance Window is configured for week day '$dayOfWeek' which is the current day of week." + } + } + + if ($on) { + + if ($on -ne 'last') { + $on = [int][string]$on[0] + } + + $daysInMonth = [datetime]::DaysInMonth($now.Year, $now.Month) + $daysInMonth = for ($i = 1; $i -le $daysInMonth; $i++) { + Get-Date -Date $now -Day $i + } + + $daysInMonth = $daysInMonth | Where-Object { $_.DayOfWeek -eq $dayOfWeek } + + $daysInMonth = if ($on -eq 'last') { + $daysInMonth | Select-Object -Last 1 + } + else { + $daysInMonth | Select-Object -Index ($on - 1) + } + + if ($daysInMonth.ToShortDateString() -ne $now.ToShortDateString()) { + Write-Host "Today is not the '$on' $dayOfWeek in the current month" + continue + } + else { + Write-Host "The LCM is supposed to run on the '$on' $dayOfWeek which applies to today" + } + } + + Write-Host "Maintenance window: $($startTime) - $($endTime)." + if ($currentTime -gt $startTime -and $currentTime -lt $endTime) { + Write-Host "Current time '$currentTime' is in maintenance window '$($maintenanceWindow.PSChildName)'" + + Write-Host "IN MAINTENANCE WINDOW: Setting 'inMaintenanceWindow' to 'true' as the current time is in a maintanence windows." + $true + break + } + else { + Write-Host "Current time '$currentTime' is not in maintenance window '$($maintenanceWindow.PSChildName)'" + } + } + } + else { + Write-Host "No maintenance windows defined. Setting 'inMaintenanceWindow' to 'false'." + $false + } + Write-Host + + if (-not $inMaintenanceWindow -and $maintenanceWindowOverride) { + Write-Host "OVERRIDE: 'inMaintenanceWindow' is 'false' but 'maintenanceWindowOverride' is enabled, setting 'inMaintenanceWindow' to 'true'" + $true + } + elseif (-not $inMaintenanceWindow) { + Write-Host "NOT IN MAINTENANCE WINDOW: 'inMaintenanceWindow' is 'false'. The current time is not in any of the $($maintenanceWindows.Count) maintenance windows." + $false + } + else { + $inMaintenanceWindow + } +} + +function Set-LcmMode { + param( + [Parameter(Mandatory = $true)] + [ValidateSet('ApplyAndAutoCorrect', 'ApplyAndMonitor')] + [string]$Mode + ) + $metaMofFolder = mkdir -Path "$path\MetaMof" -Force + + if (Test-Path -Path C:\Windows\System32\Configuration\MetaConfig.mof) { + $mofFile = Copy-Item -Path C:\Windows\System32\Configuration\MetaConfig.mof -Destination "$path\MetaMof\localhost.meta.mof" -Force -PassThru + } + else { + $mofFile = Get-Item -Path "$path\MetaMof\localhost.meta.mof" -ErrorAction Stop + } + $content = Get-Content -Path $mofFile.FullName -Raw -Encoding Unicode + + $pattern = '(ConfigurationMode(\s+)?=(\s+)?)("\w+")(;)' + $content = $content -replace $pattern, ('$1 "{0}"$5' -f $Mode) + Write-Host "LCM put into '$Mode' mode" +} + +function Set-LcmRebootNodeIfNeededMode { + param( + [Parameter(Mandatory = $true)] + [bool] $RebootNodeIfNeeded + ) + $metaMofFolder = mkdir -Path "$path\MetaMof" -Force + if (Test-Path -Path C:\Windows\System32\Configuration\MetaConfig.mof) + { + $mofFile = Copy-Item -Path C:\Windows\System32\Configuration\MetaConfig.mof -Destination "$path\MetaMof\localhost.meta.mof" -Force -PassThru + } + else + { + $mofFile = Get-Item -Path "$path\MetaMof\localhost.meta.mof" -ErrorAction Stop + } + $content = Get-Content -Path $mofFile.FullName -Raw -Encoding Unicode + + $patternRebootNodeIfNeeded = '(RebootNodeIfNeeded(\s+)?=(\s+)?)(true|false);' + $content = $content -replace $patternRebootNodeIfNeeded, ('$1 {0};' -f $RebootNodeIfNeeded) + $content | Out-File -FilePath $mofFile.FullName -Encoding unicode + Set-DscLocalConfigurationManager -Path $metaMofFolder + Write-Host "LCM RebootIfNeededValue is set to '$RebootNodeIfNeeded'" +} +function Test-StartDscAutoCorrect { + if ($maintenanceWindowMode -eq 'AutoCorrect') { + + $nextAutoCorrect = $lastAutoCorrect + $autoCorrectInterval + Write-Host "" + Write-Host "The previous AutoCorrect was done on '$lastAutoCorrect', the next one will not be triggered before '$nextAutoCorrect'. AutoCorrectInterval is $autoCorrectInterval." + if ($currentTime -gt $nextAutoCorrect) { + Write-Host 'It is time to trigger an AutoCorrect per the defined interval.' + $doAutoCorrect = $true + } + else { + if ($autoCorrectIntervalOverride) { + Write-Host "OVERRIDE: It is NOT time to trigger an AutoCorrect per the defined interval but 'AutoCorrectIntervalOverride' is enabled." + $doAutoCorrect = $true + } + else { + Write-Host 'It is NOT time to trigger an AutoCorrect per the defined interval.' + $doAutoCorrect = $false + } + } + $doAutoCorrect + } + else { + $false + } + +} + +function Test-StartDscRefresh { + if ($maintenanceWindowMode -eq 'AutoCorrect') { + + $nextRefresh = $lastRefresh + $refreshInterval + Write-Host "" + Write-Host "The previous Refresh was done on '$lastRefresh', the next one will not be triggered before '$nextRefresh'. RefreshInterval is $refreshInterval." + if ($currentTime -gt $nextRefresh) { + Write-Host 'It is time to trigger a Refresh per the defined interval.' + $doRefresh = $true + } + else { + if ($refreshIntervalOverride) { + Write-Host "OVERRIDE: It is NOT time to trigger a Refresh check per the defined interval but 'refreshIntervalOverride' is enabled." + $doRefresh = $true + } + else { + Write-Host 'It is NOT time to trigger a Refresh check per the defined interval.' + $doRefresh = $false + } + } + $doRefresh + } + else { + $false + } + +} + +function Start-AutoCorrect { + Write-Host "ACTION: Invoking Cim Method 'PerformRequiredConfigurationChecks' with Flags '1' (Consistency Check)." + try { + $script:lcmRuntime = Start-LcmRequiredConfigurationChecks -Mode AutoCorrect -MaxLcmRuntime $maxLcmRuntime -Flags 1 + $dscLcmController = Get-Item -Path HKLM:\SOFTWARE\DscLcmController + Set-ItemProperty -Path $dscLcmController.PSPath -Name LastAutoCorrect -Value (Get-Date) -Type String -Force + } + catch { + Write-Error "Error invoking 'PerformRequiredConfigurationChecks'. The message is: '$($_.Exception.Message)'" + $script:autoCorrectErrors = $true + } +} + +function Start-Monitor { + Write-Host "ACTION: Invoking Cim Method 'PerformRequiredConfigurationChecks' with Flags '1' (Consistency Check)." + try { + $script:lcmRuntime = Start-LcmRequiredConfigurationChecks -Mode Monitor -MaxLcmRuntime $maxLcmRuntime -Flags 1 + $dscLcmController = Get-Item -Path HKLM:\SOFTWARE\DscLcmController + Set-ItemProperty -Path $dscLcmController.PSPath -Name LastMonitor -Value (Get-Date) -Type String -Force + } + catch { + Write-Error "Error invoking 'PerformRequiredConfigurationChecks'. The message is: '$($_.Exception.Message)'" + $script:monitorErrors = $true + } +} + +function Start-Refresh { + Write-Host "ACTION: Invoking Cim Method 'PerformRequiredConfigurationChecks' with Flags'5' (Pull and Consistency Check)." + try { + $script:lcmRuntime = Start-LcmRequiredConfigurationChecks -Mode AutoCorrect -MaxLcmRuntime $maxLcmRuntime -Flags 5 + $dscLcmController = Get-Item -Path HKLM:\SOFTWARE\DscLcmController + Set-ItemProperty -Path $dscLcmController.PSPath -Name LastRefresh -Value (Get-Date) -Type String -Force + if ($sendDscTaggingData) { + try { + Send-DscTaggingData -ErrorAction Stop + } + catch { + $sendDscTaggingDataError = $true + } + } + } + catch { + Write-Error "Error invoking 'PerformRequiredConfigurationChecks'. The message is: '$($_.Exception.Message)'" + $script:refreshErrors = $true + } +} + +function Test-StartDscMonitor { + $nextMonitor1 = $lastMonitor + $monitorInterval + $nextMonitor2 = $lastAutoCorrect + $monitorInterval + $nextMonitor = [datetime][math]::Max($nextMonitor1.Ticks, $nextMonitor2.Ticks) + + Write-Host '' + Write-Host "The previous Monitor was done on '$lastMonitor', the next one will not be triggered before '$nextMonitor'. MonitorInterval is $monitorInterval." + if ($currentTime -gt $nextMonitor) { + Write-Host 'It is time to trigger a Monitor per the defined interval.' + $doMonitor = $true + } + else { + Write-Host 'It is NOT time to trigger a Monitor per the defined interval.' + $doMonitor = $false + } + $doMonitor +} + +function Start-LcmRequiredConfigurationChecks { + param( + [OutputType([timespan])] + [Parameter()] + [timespan]$MaxLcmRuntime = (New-TimeSpan -Days 2), + + [Parameter(Mandatory = $true)] + [ValidateSet('Monitor', 'AutoCorrect')] + [string]$Mode, + + [Parameter(Mandatory = $true)] + [int]$Flags + ) + Write-Verbose "Entering 'Start-LcmRequiredConfigurationChecks'" + + $internalMaxLcmRuntime = $MaxLcmRuntime + + $j = Start-Job -ScriptBlock { + param( + [Parameter(Mandatory = $true)] + [int]$Flags + ) + $params = @{ + ClassName = 'MSFT_DSCLocalConfigurationManager' + Namespace = 'root/Microsoft/Windows/DesiredStateConfiguration' + MethodName = 'PerformRequiredConfigurationChecks' + Arguments = @{ Flags = [uint32]$Flags } + ErrorAction = 'Stop' + } + Write-Output "Calling 'Invoke-CimMethod' with the following parameters:" + $params | ConvertTo-Json | Write-Output + Invoke-CimMethod @params | Out-Null + + } -ArgumentList $Flags + + Write-Host "Waiting $MaxLcmRuntime for the background job to finish." + + while ($j.State -eq 'Running' -and $internalMaxLcmRuntime -gt 0) { + $waitIntervalInSeconds = 5 + Start-Sleep -Seconds $waitIntervalInSeconds + $output = $j | Receive-Job | Out-String + if ($output) { $output | Write-Host } + $internalMaxLcmRuntime = $internalMaxLcmRuntime.Subtract((New-TimeSpan -Seconds $waitIntervalInSeconds)) + } + + if ($j.State -eq 'Running') { + Write-Host "LCM did not finish with the timeout of '$MaxLcmRuntime'" + $j | Stop-Job + #find the process that is hosting the DSC engine + $dscProcess = Get-CimInstance -ClassName msft_providers | Where-Object { $_.Provider -like 'dsccore' } + Write-Host "Shutting down LCM process with ID '$($dscProcess.HostProcessIdentifier)'" + Get-Process -Id $dscProcess.HostProcessIdentifier | Stop-Process -Force + if ($Mode -eq 'AutoCorrect') { + Set-ItemProperty -Path $dscLcmController.PSPath -Name LastAutoCorrect -Value (Get-Date -Date 0) -Type String -Force + } + else { + Set-ItemProperty -Path $dscLcmController.PSPath -Name LastMonitor -Value (Get-Date -Date 0) -Type String -Force + } + Write-Error -Message "LCM did run longer than '$MaxLcmRuntime'. Process was stopped." -ErrorAction Stop + } + + $runtime = $j.PSEndTime - $j.PSBeginTime + Write-Host "LCM runtime was '$runtime'" + $runtime +} + +$writeTranscripts = Get-ItemPropertyValue -Path HKLM:\SOFTWARE\DscLcmController -Name WriteTranscripts +$path = Join-Path -Path ([System.Environment]::GetFolderPath('CommonApplicationData')) -ChildPath 'Dsc\LcmController' + +if ($writeTranscripts) { + Start-Transcript -Path "$path\LcmController.log" -Append +} + +#Disable DSC Timer +$timer = Get-CimInstance -ClassName msft_providers | Where-Object { $_.Provider -like 'dsctimer' } +$timer | Invoke-CimMethod -MethodName UnLoad + +$now = Get-Date +$lcmConfiguration = Get-DscLocalConfigurationManager +$currentConfigurationMode = $lcmConfiguration.ConfigurationMode +$lcmModeChanged = '' +$doConsistencyCheck = $false +$doRefresh = $false +$inMaintenanceWindow = $false +$doAutoCorrect = $false +$doRefresh = $false +$doMonitor = $false +$autoCorrectErrors = $false +$refreshErrors = $false +$monitorErrors = $false +$currentTime = Get-Date +$lcmRuntime = $null +$sendDscTaggingData = $false +$sendDscTaggingDataError = $false +$dscLcmController = Get-Item -Path HKLM:\SOFTWARE\DscLcmController + +$maintenanceWindows = Get-ChildItem -Path HKLM:\SOFTWARE\DscLcmController\MaintenanceWindows +[bool]$maintenanceWindowOverride = Get-ItemPropertyValue -Path HKLM:\SOFTWARE\DscLcmController -Name MaintenanceWindowOverride +[timespan]$autoCorrectInterval = Get-ItemPropertyValue -Path HKLM:\SOFTWARE\DscLcmController -Name AutoCorrectInterval +[bool]$autoCorrectIntervalOverride = Get-ItemPropertyValue -Path HKLM:\SOFTWARE\DscLcmController -Name AutoCorrectIntervalOverride +[timespan]$monitorInterval = Get-ItemPropertyValue -Path HKLM:\SOFTWARE\DscLcmController -Name MonitorInterval +[timespan]$refreshInterval = Get-ItemPropertyValue -Path HKLM:\SOFTWARE\DscLcmController -Name RefreshInterval +[bool]$refreshIntervalOverride = Get-ItemPropertyValue -Path HKLM:\SOFTWARE\DscLcmController -Name RefreshIntervalOverride +[timespan]$maxLcmRuntime = Get-ItemPropertyValue -Path HKLM:\SOFTWARE\DscLcmController -Name MaxLcmRuntime +[timespan]$logHistoryTimeSpan = Get-ItemPropertyValue -Path HKLM:\SOFTWARE\DscLcmController -Name LogHistoryTimeSpan +[bool]$sendDscTaggingData = Get-ItemPropertyValue -Path HKLM:\SOFTWARE\DscLcmController -Name SendDscTaggingData +$maintenanceWindowMode = Get-ItemPropertyValue -Path HKLM:\SOFTWARE\DscLcmController -Name MaintenanceWindowMode + +[datetime]$lastAutoCorrect = try { + Get-ItemPropertyValue -Path HKLM:\SOFTWARE\DscLcmController -Name LastAutoCorrect +} +catch { + Get-Date -Date 0 +} +[datetime]$lastMonitor = try { + Get-ItemPropertyValue -Path HKLM:\SOFTWARE\DscLcmController -Name LastMonitor +} +catch { + Get-Date -Date 0 +} +[datetime]$lastRefresh = try { + Get-ItemPropertyValue -Path HKLM:\SOFTWARE\DscLcmController -Name LastRefresh +} +catch { + Get-Date -Date 0 +} +[datetime]$lastLcmPostpone = try { + Get-ItemPropertyValue -Path HKLM:\SOFTWARE\DscLcmController -Name LastLcmPostpone +} +catch { + Get-Date -Date 0 +} + +Write-Host '----------------------------------------------------------------------------' +Set-LcmPostpone + +$inMaintenanceWindow = Test-InMaintenanceWindow +Write-Host +if ($inMaintenanceWindow) { + if (!$lcmConfiguration.RebootNodeIfNeeded) + { + Write-Host "RebootNodeIfNeeded is set to 'False', but it's in maintenance window, set RebootNodeIfNeeded to 'True'" + Set-LcmRebootNodeIfNeededMode -RebootNodeIfNeeded $true + } + if ($maintenanceWindowMode -eq 'AutoCorrect' -and $currentConfigurationMode -ne 'ApplyAndAutoCorrect') { + Write-Host "MaintenanceWindowMode is '$maintenanceWindowMode' but LCM is set to '$currentConfigurationMode'. Changing LCM to 'ApplyAndAutoCorrect'" + Set-LcmMode -Mode 'ApplyAndAutoCorrect' + $lcmModeChanged = 'ApplyAndAutoCorrect' + } + elseif ($maintenanceWindowMode -eq 'Monitor' -and $currentConfigurationMode -ne 'ApplyAndMonitor') { + Write-Host "MaintenanceWindowMode is '$maintenanceWindowMode' but LCM is set to '$currentConfigurationMode'. Changing LCM to 'ApplyAndMonitor'" + Set-LcmMode -Mode 'ApplyAndMonitor' + $lcmModeChanged = 'ApplyAndMonitor' + } +} +else{ + if ($lcmConfiguration.RebootNodeIfNeeded) + { + Write-Host "RebootNodeIfNeeded is set to 'True', but it's not into maintenance window, set RebootNodeIfNeeded to 'False'" + Set-LcmRebootNodeIfNeededMode -RebootNodeIfNeeded $false + } +} +if ($inMaintenanceWindow) { + $doAutoCorrect = Test-StartDscAutoCorrect + $doRefresh = Test-StartDscRefresh + + if ($doRefresh) { + Start-Refresh + } + else { + Write-Host "NO ACTION: 'doRefresh' is false, not invoking Cim Method 'PerformRequiredConfigurationChecks' with Flags '5' (Pull and Consistency Check)." + } + + if ($doAutoCorrect) { + Start-AutoCorrect + } + else { + Write-Host "NO ACTION: 'doAutoCorrect' is false, not invoking Cim Method 'PerformRequiredConfigurationChecks' with Flags '1' (Consistency Check)." + } + +} + +Write-Host +if ($lcmModeChanged) { + Write-Host "Setting LCM back from '$lcmModeChanged' to '$currentConfigurationMode'." + Set-LcmMode -Mode $currentConfigurationMode +} + +Write-Host +if (-not $doAutoCorrect) { + $doMonitor = Test-StartDscMonitor + if ($doMonitor) { + Start-Monitor + } + else { + Write-Host "NO ACTION: 'doMonitor' is false, not invoking Cim Method 'PerformRequiredConfigurationChecks' with Flags '1' (Consistency Check)." + } +} +else { + Write-Host "In AutoCorrect mode, skipping Montior" +} + +$logItem = [pscustomobject]@{ + CurrentTime = (Get-Date).ToString('M\/d\/yyyy h:m:s tt', [System.Globalization.CultureInfo]::InvariantCulture) + InMaintenanceWindow = [int]$inMaintenanceWindow + DoAutoCorrect = [int]$doAutoCorrect + DoMonitor = [int]$doMonitor + DoRefresh = [int]$doRefresh + + LastAutoCorrect = $lastAutoCorrect.ToString('M\/d\/yyyy h:m:s tt', [System.Globalization.CultureInfo]::InvariantCulture) + LastMonitor = $lastMonitor.ToString('M\/d\/yyyy h:m:s tt', [System.Globalization.CultureInfo]::InvariantCulture) + AutoCorrectInterval = $autoCorrectInterval + AutoCorrectIntervalOverride = $autoCorrectIntervalOverride + ConsistencyCheckErrors = $autoCorrectErrors + + MonitorInterval = $monitorInterval + MonitorErrors = $monitorErrors + + LastRefresh = $lastRefresh.ToString('M\/d\/yyyy h:m:s tt', [System.Globalization.CultureInfo]::InvariantCulture) + RefreshInterval = $refreshInterval + RefreshIntervalOverride = $refreshIntervalOverride + RefreshErrors = $refreshErrors + + MaxLcmRuntime = $maxLcmRuntime + LcmRuntime = $lcmRuntime + + SendDscTaggingDataError = $sendDscTaggingDataError + +} | Export-Csv -Path "$path\LcmControllerSummary.csv" -Delimiter ',' -Append -Force + +if ($writeTranscripts) { + Stop-Transcript +} + +#------------------------ LcmController.log cleanup ---------------------------------- + +$pattern = '(\*{22}\r\nWindows PowerShell transcript start\r\n)((.|\r\n)+?)(End time: \d{14}\r\n\*{22})' +$date = (Get-Date) - $logHistoryTimeSpan + +$lcmControllerLogContent = Get-Content -Path "$path\LcmController.log" -Raw +$regexMatches = [regex]::Matches($lcmControllerLogContent, $pattern) + +$logEntries = $regexMatches | Where-Object { + [datetime]::ParseExact((($_.Value -split "\n")[-2] -split ' ')[2].Trim(), 'yyyyMMddHHmmss', $null) -gt $date +} + +#$logEntries | Group-Object -Property { [datetime]::ParseExact((($_.Value -split "\n")[-2] -split ' ')[2].Trim(),'yyyyMMddHHmmss',$null).ToString('yy MM dd') } + +Write-Host "Log file contained $($regexMatches.Count) entries, after cleanup if contains $($logEntries.Count) entries." +$logEntries.Value | Out-File -FilePath "$path\LcmController.log" -Force + +#------------------ LcmControllerSummary.csv cleanup ------------------------------ + +$summaryContent = Import-Csv -Path "$path\LcmControllerSummary.csv" -Delimiter ',' +$filteredSummaryContent = $summaryContent | Where-Object { [datetime]$_.CurrentTime -gt $date } + +Write-Host "Summary file contained $($summaryContent.Count) entries, after cleanup if contains $($filteredSummaryContent.Count) entries." +$filteredSummaryContent | Export-Csv -Path "$path\LcmControllerSummary.csv" -Delimiter ',' -Force +'@ + +configuration DscLcmController { + param ( + [Parameter(Mandatory = $true)] + [ValidateSet('Monitor', 'AutoCorrect')] + [string] + $MaintenanceWindowMode, + + [Parameter(Mandatory = $true)] + [timespan] + $MonitorInterval, + + [Parameter(Mandatory = $true)] + [timespan] + $AutoCorrectInterval, + + [Parameter()] + [bool] + $AutoCorrectIntervalOverride, + + [Parameter(Mandatory = $true)] + [timespan] + $RefreshInterval, + + [Parameter()] + [bool] + $RefreshIntervalOverride, + + [Parameter(Mandatory = $true)] + [timespan] + $ControllerInterval, + + [Parameter()] + [bool] + $MaintenanceWindowOverride, + + [Parameter()] + [timespan] + $MaxLcmRuntime = (New-TimeSpan -Days 2), + + [Parameter()] + [timespan] + $LogHistoryTimeSpan = (New-TimeSpan -Days 90), + + [Parameter()] + [bool] + $SendDscTaggingData, + + [Parameter()] + [bool] + $WriteTranscripts + ) + + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName xPSDesiredStateConfiguration + Import-DscResource -ModuleName ComputerManagementDsc + + xRegistry DscLcmController_MaintenanceWindowMode + { + Key = 'HKEY_LOCAL_MACHINE\SOFTWARE\DscLcmController' + ValueName = 'MaintenanceWindowMode' + ValueData = $MaintenanceWindowMode + ValueType = 'String' + Ensure = 'Present' + Force = $true + } + + xRegistry DscLcmController_MonitorInterval + { + Key = 'HKEY_LOCAL_MACHINE\SOFTWARE\DscLcmController' + ValueName = 'MonitorInterval' + ValueData = $MonitorInterval + ValueType = 'String' + Ensure = 'Present' + Force = $true + } + + xRegistry DscLcmController_AutoCorrectInterval + { + Key = 'HKEY_LOCAL_MACHINE\SOFTWARE\DscLcmController' + ValueName = 'AutoCorrectInterval' + ValueData = $AutoCorrectInterval + ValueType = 'String' + Ensure = 'Present' + Force = $true + } + + xRegistry DscLcmController_AutoCorrectIntervalOverride + { + Key = 'HKEY_LOCAL_MACHINE\SOFTWARE\DscLcmController' + ValueName = 'AutoCorrectIntervalOverride' + ValueData = [int]$AutoCorrectIntervalOverride + ValueType = 'DWord' + Ensure = 'Present' + Force = $true + } + + xRegistry DscLcmController_RefreshInterval + { + Key = 'HKEY_LOCAL_MACHINE\SOFTWARE\DscLcmController' + ValueName = 'RefreshInterval' + ValueData = $RefreshInterval + ValueType = 'String' + Ensure = 'Present' + Force = $true + } + + xRegistry DscLcmController_RefreshIntervalOverride + { + Key = 'HKEY_LOCAL_MACHINE\SOFTWARE\DscLcmController' + ValueName = 'RefreshIntervalOverride' + ValueData = [int]$RefreshIntervalOverride + ValueType = 'DWord' + Ensure = 'Present' + Force = $true + } + + xRegistry DscLcmController_ControllerInterval + { + Key = 'HKEY_LOCAL_MACHINE\SOFTWARE\DscLcmController' + ValueName = 'ControllerInterval' + ValueData = $ControllerInterval + ValueType = 'String' + Ensure = 'Present' + Force = $true + } + + xRegistry DscLcmController_MaintenanceWindowOverride + { + Key = 'HKEY_LOCAL_MACHINE\SOFTWARE\DscLcmController' + ValueName = 'MaintenanceWindowOverride' + ValueData = [int]$MaintenanceWindowOverride + ValueType = 'DWord' + Ensure = 'Present' + Force = $true + } + + xRegistry DscLcmController_WriteTranscripts + { + Key = 'HKEY_LOCAL_MACHINE\SOFTWARE\DscLcmController' + ValueName = 'WriteTranscripts' + ValueData = [int]$WriteTranscripts + ValueType = 'DWord' + Ensure = 'Present' + Force = $true + } + + xRegistry DscLcmController_MaxLcmRuntime + { + Key = 'HKEY_LOCAL_MACHINE\SOFTWARE\DscLcmController' + ValueName = 'MaxLcmRuntime' + ValueData = $MaxLcmRuntime + ValueType = 'String' + Ensure = 'Present' + Force = $true + } + + xRegistry DscLcmController_LogHistoryTimeSpan + { + Key = 'HKEY_LOCAL_MACHINE\SOFTWARE\DscLcmController' + ValueName = 'LogHistoryTimeSpan' + ValueData = $LogHistoryTimeSpan + ValueType = 'String' + Ensure = 'Present' + Force = $true + } + + xRegistry DscLcmController_SendDscTaggingData + { + Key = 'HKEY_LOCAL_MACHINE\SOFTWARE\DscLcmController' + ValueName = 'SendDscTaggingData' + ValueData = [int]$SendDscTaggingData + ValueType = 'DWord' + Ensure = 'Present' + Force = $true + } + + File DscLcmControllerScript + { + Ensure = 'Present' + Type = 'File' + DestinationPath = 'C:\ProgramData\Dsc\LcmController\LcmController.ps1' + Contents = $dscLcmControllerScript + } + + ScheduledTask DscControllerTask + { + DependsOn = '[File]DscLcmControllerScript' + TaskName = 'DscLcmController' + TaskPath = '\DscController' + ActionExecutable = 'C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe' + ActionArguments = '-File C:\ProgramData\Dsc\LcmController\LcmController.ps1' + ScheduleType = 'Once' + RepeatInterval = $ControllerInterval + RepetitionDuration = 'Indefinitely' + StartTime = (Get-Date) + } +} diff --git a/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/DscLcmMaintenanceWindows/DscLcmMaintenanceWindows.psd1 b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/DscLcmMaintenanceWindows/DscLcmMaintenanceWindows.psd1 new file mode 100644 index 00000000..19f5c96f --- /dev/null +++ b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/DscLcmMaintenanceWindows/DscLcmMaintenanceWindows.psd1 @@ -0,0 +1,15 @@ +@{ + RootModule = 'DscLcmMaintenanceWindows.schema.psm1' + + ModuleVersion = '0.0.1' + + GUID = 'f89ef950-808a-4698-ad82-649352863022' + + Author = 'NA' + + CompanyName = 'NA' + + Copyright = 'NA' + + DscResourcesToExport = @('DscLcmMaintenanceWindows') +} diff --git a/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/DscLcmMaintenanceWindows/DscLcmMaintenanceWindows.schema.psm1 b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/DscLcmMaintenanceWindows/DscLcmMaintenanceWindows.schema.psm1 new file mode 100644 index 00000000..46972af7 --- /dev/null +++ b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/DscLcmMaintenanceWindows/DscLcmMaintenanceWindows.schema.psm1 @@ -0,0 +1,102 @@ +configuration DscLcmMaintenanceWindows { + param ( + [Parameter(Mandatory = $true)] + [hashtable[]]$MaintenanceWindows + ) + + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName xPSDesiredStateConfiguration + + $on = '1st', '2nd', '3rd', '4th', 'last' + $daysOfWeek = [System.Enum]::GetNames([System.DayOfWeek]) + + foreach ($window in $MaintenanceWindows.GetEnumerator()) + { + if ($window.DayOfWeek) + { + if ($window.DayOfWeek -notin $daysOfWeek) + { + Write-Error "DayOfWeek '$($window.DayOfWeek)' of maintenance window '$($window.Name)' is not in the supported range ('$($daysOfWeek -join ', ')')." + } + } + + if ($window.On) + { + if ($window.On -notin $on) + { + Write-Error "Property 'On' set to '$($window.On)' of maintenance window '$($window.Name)' is not in the supported range ('$($on -join ', ')')." + } + } + } + + Script MaintenanceWindowsCheck + { + TestScript = { + try + { + $existingWindows = Get-ChildItem -Path HKLM:\SOFTWARE\DscLcmController\MaintenanceWindows -ErrorAction Stop | Select-Object -ExpandProperty PSChildName + $diff = Compare-Object -ReferenceObject $existingWindows -DifferenceObject $using:MaintenanceWindows.Name + Write-Verbose "Result: $([bool]-not $diff) - $diff" + [bool]-not $diff + } + catch + { + Write-Verbose "Result: False`n$_" + $false + } + } + SetScript = { + Write-Verbose 'There is a difference in the maintainance window definition. Removing currently configured maintenance windows.' + Remove-Item -Path HKLM:\SOFTWARE\DscLcmController\MaintenanceWindows -Force -Recurse -ErrorAction SilentlyContinue + } + + GetScript = { + @{ + Result = Get-ChildItem -Path HKLM:\SOFTWARE\DscLcmController\MaintenanceWindows -ErrorAction SilentlyContinue | Select-Object -ExpandProperty PSChildName + } + } + } + + foreach ($window in $MaintenanceWindows.GetEnumerator()) + { + xRegistry "StartTime_$($window.Name)" + { + Key = "HKEY_LOCAL_MACHINE\SOFTWARE\DscLcmController\MaintenanceWindows\$($window.Name)" + ValueName = 'StartTime' + ValueData = $window.StartTime + ValueType = 'String' + Ensure = 'Present' + Force = $true + } + + xRegistry "Timespan_$($window.Name)" + { + Key = "HKEY_LOCAL_MACHINE\SOFTWARE\DscLcmController\MaintenanceWindows\$($window.Name)" + ValueName = 'Timespan' + ValueData = $window.Timespan + ValueType = 'String' + Ensure = 'Present' + Force = $true + } + + xRegistry "DayOfWeek_$($window.Name)" + { + Key = "HKEY_LOCAL_MACHINE\SOFTWARE\DscLcmController\MaintenanceWindows\$($window.Name)" + ValueName = 'DayOfWeek' + ValueData = $window.DayOfWeek + ValueType = 'String' + Ensure = 'Present' + Force = $true + } + + xRegistry "On_$($window.Name)" + { + Key = "HKEY_LOCAL_MACHINE\SOFTWARE\DscLcmController\MaintenanceWindows\$($window.Name)" + ValueName = 'On' + ValueData = $window.On + ValueType = 'String' + Ensure = 'Present' + Force = $true + } + } +} diff --git a/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/DscTagging/DscTagging.psd1 b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/DscTagging/DscTagging.psd1 new file mode 100644 index 00000000..9f8b79a7 --- /dev/null +++ b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/DscTagging/DscTagging.psd1 @@ -0,0 +1,15 @@ +@{ + RootModule = 'DscTagging.schema.psm1' + + ModuleVersion = '0.0.1' + + GUID = 'c25304c4-f319-4963-ba13-4a77ee15f151' + + Author = 'NA' + + CompanyName = 'NA' + + Copyright = 'NA' + + DscResourcesToExport = @('DscTagging') +} diff --git a/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/DscTagging/DscTagging.schema.psm1 b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/DscTagging/DscTagging.schema.psm1 new file mode 100644 index 00000000..1c384182 --- /dev/null +++ b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/DscTagging/DscTagging.schema.psm1 @@ -0,0 +1,137 @@ +configuration DscTagging { + param ( + [Parameter(Mandatory = $true)] + [System.Version] + $Version, + + [Parameter(Mandatory = $true)] + [string] + $Environment, + + [Parameter()] + [string] + $NodeVersion, + + [Parameter()] + [string] + $NodeRole, + + [Parameter()] + [boolean] + $DisableGitCommitId = $false, + + [Parameter()] + [string[]] + $Layers + ) + + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName xPSDesiredStateConfiguration + + if ($DisableGitCommitId -ne $false) + { + $gitCommitId = git log -n 1 *>&1 + $gitCommitId = if ($gitCommitId -like '*fatal*') + { + 'NoGitRepo' + } + else + { + $gitCommitId[0].Substring(7) + } + + xRegistry DscGitCommitId + { + Key = 'HKEY_LOCAL_MACHINE\SOFTWARE\DscTagging' + ValueName = 'GitCommitId' + ValueData = $gitCommitId + ValueType = 'String' + Ensure = 'Present' + Force = $true + } + } + + xRegistry DscVersion + { + Key = 'HKEY_LOCAL_MACHINE\SOFTWARE\DscTagging' + ValueName = 'Version' + ValueData = $Version + ValueType = 'String' + Ensure = 'Present' + Force = $true + } + + xRegistry DscEnvironment + { + Key = 'HKEY_LOCAL_MACHINE\SOFTWARE\DscTagging' + ValueName = 'Environment' + ValueData = $Environment + ValueType = 'String' + Ensure = 'Present' + Force = $true + } + + xRegistry DscBuildDate + { + Key = 'HKEY_LOCAL_MACHINE\SOFTWARE\DscTagging' + ValueName = 'BuildDate' + ValueData = [string](Get-Date) + ValueType = 'String' + Ensure = 'Present' + Force = $true + } + + xRegistry DscBuildNumber + { + Key = 'HKEY_LOCAL_MACHINE\SOFTWARE\DscTagging' + ValueName = 'BuildNumber' + ValueData = "$($env:BHBuildNumber)" + ValueType = 'String' + Ensure = 'Present' + Force = $true + } + + if (-not [string]::IsNullOrWhiteSpace($NodeVersion)) + { + xRegistry DscNodeVersion + { + Key = 'HKEY_LOCAL_MACHINE\SOFTWARE\DscTagging' + ValueName = 'NodeVersion' + ValueData = $NodeVersion + ValueType = 'String' + Ensure = 'Present' + Force = $true + } + } + + if (-not [string]::IsNullOrWhiteSpace($NodeRole)) + { + xRegistry DscNodeRole + { + Key = 'HKEY_LOCAL_MACHINE\SOFTWARE\DscTagging' + ValueName = 'NodeRole' + ValueData = $NodeRole + ValueType = 'String' + Ensure = 'Present' + Force = $true + } + } + + if ($null -ne $Layers -and $Layers.Count -gt 0) + { + # check for duplicate layers + $ht = @{} + $Layers | ForEach-Object { $ht["$_"] += 1 } + $ht.Keys | Where-Object { $ht["$_"] -gt 1 } | ForEach-Object { throw "ERROR: DscTagging: Duplicate layer '$_' found." } + + xRegistry DscModules + { + Key = 'HKEY_LOCAL_MACHINE\SOFTWARE\DscTagging' + ValueName = 'Layers' + ValueData = $Layers + ValueType = 'MultiString' + Ensure = 'Present' + Force = $true + } + } +} diff --git a/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/EnvironmentVariables/EnvironmentVariables.psd1 b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/EnvironmentVariables/EnvironmentVariables.psd1 new file mode 100644 index 00000000..d2cedaf8 --- /dev/null +++ b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/EnvironmentVariables/EnvironmentVariables.psd1 @@ -0,0 +1,15 @@ +@{ + RootModule = 'EnvironmentVariables.schema.psm1' + + ModuleVersion = '0.0.1' + + GUID = '4fd4254c-bea0-4d30-9e7f-acb95e022a85' + + Author = 'NA' + + CompanyName = 'NA' + + Copyright = 'NA' + + DscResourcesToExport = @('EnvironmentVariables') +} diff --git a/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/EnvironmentVariables/EnvironmentVariables.schema.psm1 b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/EnvironmentVariables/EnvironmentVariables.schema.psm1 new file mode 100644 index 00000000..a20b844e --- /dev/null +++ b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/EnvironmentVariables/EnvironmentVariables.schema.psm1 @@ -0,0 +1,35 @@ +configuration EnvironmentVariables { + param ( + [Parameter()] + [hashtable[]] + $Variables + ) + +<# +xEnvironment [String] #ResourceName +{ + Name = [string] + [DependsOn = [string[]]] + [Ensure = [string]{ Absent | Present }] + [Path = [bool]] + [PsDscRunAsCredential = [PSCredential]] + [Target = [string[]]{ Machine | Process }] + [Value = [string]] +} +#> + + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName xPSDesiredStateConfiguration + + foreach ($variable in $Variables) + { + $variable = @{} + $variable + + if (-not $variable.ContainsKey('Ensure')) + { + $variable.Ensure = 'Present' + } + + (Get-DscSplattedResource -ResourceName xEnvironment -ExecutionName $variable.Name -Properties $variable -NoInvoke).Invoke($variable) + } +} diff --git a/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/FileSystemObjects/FileSystemObjects.psd1 b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/FileSystemObjects/FileSystemObjects.psd1 new file mode 100644 index 00000000..ee2966e8 --- /dev/null +++ b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/FileSystemObjects/FileSystemObjects.psd1 @@ -0,0 +1,15 @@ +@{ + RootModule = 'FileSystemObjects.schema.psm1' + + ModuleVersion = '0.0.1' + + GUID = 'ebbefb97-3d1a-4b11-bc05-4530525f5b6c' + + Author = 'NA' + + CompanyName = 'NA' + + Copyright = 'NA' + + DscResourcesToExport = @('FileSystemObjects') +} diff --git a/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/FileSystemObjects/FileSystemObjects.schema.psm1 b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/FileSystemObjects/FileSystemObjects.schema.psm1 new file mode 100644 index 00000000..0e579351 --- /dev/null +++ b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/FileSystemObjects/FileSystemObjects.schema.psm1 @@ -0,0 +1,39 @@ +configuration FileSystemObjects +{ + param ( + [Parameter()] + [hashtable[]] + $Items + ) + + <# + FileSystemObject [String] #ResourceName + { + DestinationPath = [string] + [Checksum = [string]{ CreationTime | LastModifiedTime | md5 }] + [Contents = [string]] + [DependsOn = [string[]]] + [Encoding = [string]{ ASCII | BigEndianUnicode | Default | Latin1 | Unicode | UTF32 | UTF7 | UTF8 }] + [Ensure = [string]{ absent | present }] + [Force = [bool]] + [Group = [string]] + [IgnoreTrailingWhitespace = [bool]] + [Links = [string]{ follow | manage }] + [Mode = [string]] + [Owner = [string]] + [PsDscRunAsCredential = [PSCredential]] + [Recurse = [bool]] + [SourcePath = [string]] + [Type = [string]{ directory | file | symboliclink }] + } + #> + + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName FileSystemDsc + + foreach ($item in $Items) + { + $executionName = "FileSystemObject_$($item.DestinationPath)" -replace '[\s(){}/\\:-]', '_' + (Get-DscSplattedResource -ResourceName FileSystemObject -ExecutionName $executionName -Properties $item -NoInvoke).Invoke($item) + } +} diff --git a/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/FilesAndFolders/FilesAndFolders.psd1 b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/FilesAndFolders/FilesAndFolders.psd1 new file mode 100644 index 00000000..632e0446 --- /dev/null +++ b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/FilesAndFolders/FilesAndFolders.psd1 @@ -0,0 +1,15 @@ +@{ + RootModule = 'FilesAndFolders.schema.psm1' + + ModuleVersion = '0.0.1' + + GUID = '89446cc0-0e57-41c9-809e-de0fe6a13fec' + + Author = 'NA' + + CompanyName = 'NA' + + Copyright = 'NA' + + DscResourcesToExport = @('FilesAndFolders') +} diff --git a/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/FilesAndFolders/FilesAndFolders.schema.psm1 b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/FilesAndFolders/FilesAndFolders.schema.psm1 new file mode 100644 index 00000000..0d68f4b7 --- /dev/null +++ b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/FilesAndFolders/FilesAndFolders.schema.psm1 @@ -0,0 +1,145 @@ +configuration FilesAndFolders +{ + param ( + [Parameter(Mandatory = $true)] + [hashtable[]]$Items + ) + + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName FileSystemDsc + + foreach ($item in $Items) + { + [string]$fileHash = $null + [string]$base64Content = $null + + $permissions = $null + + # Remove Case Sensitivity of ordered Dictionary or Hashtables + $item = @{} + $item + + if (-not $item.ContainsKey('Ensure')) + { + $item.Ensure = 'Present' + } + + if ($item.ContainsKey('Permissions')) + { + $permissions = $item.Permissions + $item.Remove('Permissions') + } + + if ($item.ContainsKey('ContentFromFile')) + { + if ( -not (Test-Path -Path $item.ContentFromFile) ) + { + throw "ERROR: Content file '$($item.ContentFromFile)' not found. Current working directory is: $(Get-Location)" + } + else + { + if ( [string]::IsNullOrWhiteSpace( $item.Type ) -or $item.Type -eq 'File' ) + { + [string]$content = Get-Content -Path $item.ContentFromFile -Raw + $item.Contents += $content + $item.Type = 'File' + } + elseif ( $item.Type -eq 'BinaryFile' ) + { + $filePath = Resolve-Path $item.ContentFromFile + $fileHash = (Get-FileHash -Path $filePath -Algorithm SHA256).Hash + $base64Content = [Convert]::ToBase64String([IO.File]::ReadAllBytes($filePath)) + } + else + { + throw "ERROR: Type '$($item.Type)' is not supported with embedding file content of '$($item.ContentFromFile)'." + } + } + $item.Remove('ContentFromFile') + } + + $executionName = "file_$($item.DestinationPath)" -replace '[\s(){}/\\:-]', '_' + + if ($item.Type -ne 'BinaryFile') + { + (Get-DscSplattedResource -ResourceName File -ExecutionName $executionName -Properties $item -NoInvoke).Invoke($item) + } + else + { + if ( [string]::IsNullOrWhiteSpace( $fileHash ) -or [string]::IsNullOrWhiteSpace( $base64Content ) ) + { + throw "ERROR: Type 'BinaryFile' requires an valid attribute 'ContentFromFile'." + } + + [string]$destPath = $item.DestinationPath + [string]$ensure = $item.Ensure + + Script $executionName + { + TestScript = { + Write-Verbose "Testing file '$using:destPath'..." + if ( (Test-Path -Path $using:destPath) ) + { + Write-Verbose "Verifying file content..." + if ( $using:fileHash -eq (Get-FileHash -Path $using:destPath -Algorithm SHA256).Hash ) + { + Write-Verbose "OK" + return $true + } + } + elseif ( $using:ensure -eq 'Absent' ) + { + Write-Verbose "OK (absent)" + return $true + } + Write-Verbose "Not OK" + return $false + } + SetScript = { + if ( $using:ensure -eq 'Absent' ) + { + Write-Verbose "Removing file '$using:destPath'..." + Remove-Item -Path $using:destPath -Force + } + else + { + $dirName = [System.IO.Path]::GetDirectoryName($using:destPath) + if ( -not (Test-Path -Path $dirName) ) + { + Write-Verbose "Creating directory '$dirName'..." + New-Item -Path $dirName -ItemType Directory -Force + } + Write-Verbose "Writing file '$using:destPath'..." + Remove-Item -Path $using:destPath -Force -ErrorAction SilentlyContinue + [IO.File]::WriteAllBytes($using:destPath, [Convert]::FromBase64String($using:base64Content)) + } + } + GetScript = { + return @{ + result = 'N/A' + } + } + } + } + + if ($null -ne $permissions) + { + foreach ($perm in $permissions) + { + # Remove Case Sensitivity of ordered Dictionary or Hashtables + $perm = @{} + $perm + + $perm.Path = $item.DestinationPath + $perm.DependsOn = "[File]$executionName" + + if (-not $perm.ContainsKey('Ensure')) + { + $perm.Ensure = 'Present' + } + + $permExecName = "$($executionName)__$($perm.Identity)" -replace '[\s(){}/\\:-]', '_' + + (Get-DscSplattedResource -ResourceName FileSystemAccessRule -ExecutionName $permExecName -Properties $perm -NoInvoke).Invoke($perm) + } + } + } +} diff --git a/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/LocalGroups/LocalGroups.psd1 b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/LocalGroups/LocalGroups.psd1 new file mode 100644 index 00000000..0acb58fd --- /dev/null +++ b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/LocalGroups/LocalGroups.psd1 @@ -0,0 +1,15 @@ +@{ + RootModule = 'LocalGroups.schema.psm1' + + ModuleVersion = '0.0.1' + + GUID = '5d759160-cf7d-4bac-994b-a69d1a4cf0b0' + + Author = 'NA' + + CompanyName = 'NA' + + Copyright = 'NA' + + DscResourcesToExport = @('LocalGroups') +} diff --git a/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/LocalGroups/LocalGroups.schema.psm1 b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/LocalGroups/LocalGroups.schema.psm1 new file mode 100644 index 00000000..21c5811b --- /dev/null +++ b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/LocalGroups/LocalGroups.schema.psm1 @@ -0,0 +1,15 @@ +configuration LocalGroups { + param ( + [Parameter()] + [hashtable[]] + $Groups + ) + + Import-DscResource -ModuleName xPSDesiredStateConfiguration + + foreach ($group in $Groups) + { + $executionName = $group.GroupName -replace '[\s(){}/\\:-]', '_' + (Get-DscSplattedResource -ResourceName xGroup -ExecutionName $executionName -Properties $group -NoInvoke).Invoke($group) + } +} diff --git a/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/LocalUsers/LocalUsers.psd1 b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/LocalUsers/LocalUsers.psd1 new file mode 100644 index 00000000..e5212c32 --- /dev/null +++ b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/LocalUsers/LocalUsers.psd1 @@ -0,0 +1,15 @@ +@{ + RootModule = 'LocalUsers.schema.psm1' + + ModuleVersion = '0.0.1' + + GUID = '705309ad-b027-446f-9ad5-974ba07eafb2' + + Author = 'NA' + + CompanyName = 'NA' + + Copyright = 'NA' + + DscResourcesToExport = @('LocalUserss') +} diff --git a/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/LocalUsers/LocalUsers.schema.psm1 b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/LocalUsers/LocalUsers.schema.psm1 new file mode 100644 index 00000000..ba647195 --- /dev/null +++ b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/LocalUsers/LocalUsers.schema.psm1 @@ -0,0 +1,80 @@ +configuration LocalUsers { + param ( + [Parameter()] + [hashtable[]] + $Users + ) + + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName xPSDesiredStateConfiguration + + function AddMemberOf + { + param ( + [Parameter()] + [string] + $ExecutionName, + + [Parameter()] + [string] + $ExecutionType, + + [Parameter()] + [string] + $AccountName, + + [Parameter()] + [string[]] + $MemberOf + ) + + if ( $null -ne $MemberOf -and $MemberOf.Count -gt 0 ) + { + Script "$($ExecutionName)_MemberOf" + { + TestScript = + { + # get current member groups of the local user + $currentGroups = Get-LocalGroup | Where-Object { (Get-LocalGroupMember $_ -Member $using:AccountName -ErrorAction SilentlyContinue).Count -eq 1 } | Select-Object -ExpandProperty Name + + Write-Verbose "Principal '$using:AccountName' is member of local groups: $($currentGroups -join ', ')" + + $missingGroups = $using:MemberOf | Where-Object { -not ($currentGroups -contains $_) } + + if ( $missingGroups.Count -eq 0 ) + { + return $true + } + + Write-Verbose "Principal '$using:AccountName' is not member of required local groups: $($missingGroups -join ', ')" + return $false + } + SetScript = + { + $missingGroups = $using:MemberOf | Where-Object { (Get-LocalGroupMember $_ -Member $using:AccountName -ErrorAction SilentlyContinue).Count -eq 0 } + + Write-Verbose "Adding principal '$using:AccountName' to local groups: $($missingGroups -join ', ')" + + foreach ( $group in $missingGroups ) + { + Add-LocalGroupMember -Group $group -Member $using:AccountName -Verbose + } + } + GetScript = { return 'NA' } + DependsOn = "[$ExecutionType]$ExecutionName" + } + } + } + + foreach ($user in $Users) + { + # save group list + $memberOf = $user.MemberOf + $user.Remove( 'MemberOf' ) + + $executionName = "localUser_$($user.UserName)" -replace '[\s(){}/\\:-]', '_' + (Get-DscSplattedResource -ResourceName xUser -ExecutionName $executionName -Properties $user -NoInvoke).Invoke($user) + + AddMemberOf -ExecutionName $executionName -ExecutionType xUser -AccountName $user.UserName -MemberOf $memberOf + } +} diff --git a/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/NetworkIpConfiguration/NetworkIpConfiguration.psd1 b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/NetworkIpConfiguration/NetworkIpConfiguration.psd1 new file mode 100644 index 00000000..62f8df4d --- /dev/null +++ b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/NetworkIpConfiguration/NetworkIpConfiguration.psd1 @@ -0,0 +1,15 @@ +@{ + RootModule = 'NetworkIpConfiguration.schema.psm1' + + ModuleVersion = '0.0.1' + + GUID = 'a17a202a-e7f1-4279-aa73-3e3aadbc461c' + + Author = 'NA' + + CompanyName = 'NA' + + Copyright = 'NA' + + DscResourcesToExport = @('NetworkIpConfiguration') +} diff --git a/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/NetworkIpConfiguration/NetworkIpConfiguration.schema.psm1 b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/NetworkIpConfiguration/NetworkIpConfiguration.schema.psm1 new file mode 100644 index 00000000..bc2e127c --- /dev/null +++ b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/NetworkIpConfiguration/NetworkIpConfiguration.schema.psm1 @@ -0,0 +1,378 @@ +configuration NetworkIpConfiguration { + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidGlobalVars', '')] + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '')] + + param ( + [Parameter()] + [boolean] + $DisableNetBios = $false, + + [Parameter()] + [int16] + $ConfigureIPv6 = -1, # < 0 -> no configuration code will be generated + + [Parameter()] + [hashtable[]] + $Interfaces, + + [Parameter()] + [hashtable[]] + $Routes + ) + + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName xPSDesiredStateConfiguration + Import-DscResource -ModuleName NetworkingDsc + + function NetIpInterfaceConfig + { + param ( + [Parameter()] + [string] + $InterfaceAlias, + + [Parameter()] + [string] + $IpAddress, + + [Parameter()] + [int]$Prefix, + + [Parameter()] + [string]$Gateway, + + [Parameter()] + [string[]] + $DnsServer, + + [Parameter()] + [uint32] + $InterfaceMetric, + + [Parameter()] + [boolean] + $DisableNetbios, + + [Parameter()] + [boolean] + $EnableDhcp, + + [Parameter()] + [boolean] + $EnableLmhostsLookup, + + [Parameter()] + [boolean] + $DisableIPv6, + + [Parameter()] + [ValidateSet('Public', 'Private', 'DomainAuthenticated')] + [string] + $NetworkCategory + ) + + if ( $EnableDhcp -eq $true ) + { + if (-not [string]::IsNullOrWhiteSpace($IpAddress) -or + -not [string]::IsNullOrWhiteSpace($Gateway) -or + ($null -ne $DnsServer -and $DnsServer.Count -gt 0)) + { + throw "ERROR: Enabled DHCP requires empty 'IpAddress' ($IpAddress), 'Gateway' ($Gateway) and 'DnsServer' ($DnsServer) parameters for interface '$InterfaceAlias'." + } + + NetIPInterface "EnableDhcp_$InterfaceAlias" + { + InterfaceAlias = $InterfaceAlias + AddressFamily = 'IPv4' + Dhcp = 'Enabled' + } + + DnsServerAddress "EnableDhcpDNS_$InterfaceAlias" + { + InterfaceAlias = $InterfaceAlias + AddressFamily = 'IPv4' + } + } + else + { + if (-not [string]::IsNullOrWhiteSpace($IpAddress)) + { + # disable DHCP if IP-Address is specified + NetIPInterface "DisableDhcp_$InterfaceAlias" + { + InterfaceAlias = $InterfaceAlias + AddressFamily = 'IPv4' + Dhcp = 'Disabled' + } + + if ( -not ($Prefix -match '^\d+$') ) + { + throw "ERROR: Valid 'Prefix' parameter is required for IP address '$IpAddress'." + } + + $ip = "$($IpAddress)/$($Prefix)" + + IPAddress "NetworkIp_$InterfaceAlias" + { + IPAddress = $ip + AddressFamily = 'IPv4' + InterfaceAlias = $InterfaceAlias + } + } + + if (-not [string]::IsNullOrWhiteSpace($Gateway)) + { + DefaultGatewayAddress "DefaultGateway_$InterfaceAlias" + { + AddressFamily = 'IPv4' + InterfaceAlias = $InterfaceAlias + Address = $Gateway + } + } + + if ($null -ne $DnsServer -and $DnsServer.Count -gt 0) + { + DnsServerAddress "DnsServers_$InterfaceAlias" + { + InterfaceAlias = $InterfaceAlias + AddressFamily = 'IPv4' + Address = $DnsServer + } + } + } + + if ($null -ne $InterfaceMetric -and $InterfaceMetric -gt 0) + { + Script "InterfaceMetric_$InterfaceAlias" + { + TestScript = + { + $netIf = Get-NetIpInterface -InterfaceAlias $using:InterfaceAlias -ErrorAction SilentlyContinue + if ( $null -eq $netIf ) + { + Write-Verbose "NetIpInterface '$using:InterfaceAlias' not found." + return $false + } + + [boolean]$result = $true + $netIf | ForEach-Object { Write-Verbose "InterfaceMetric $($_.AddressFamily): $($_.InterfaceMetric)"; + if ( $_.InterfaceMetric -ne $using:InterfaceMetric ) + { + $result = $false + }; } + + Write-Verbose "Expected Interface Metric: $using:InterfaceMetric" + return $result + } + SetScript = + { + $netIf = Get-NetIpInterface -InterfaceAlias $using:InterfaceAlias + + $netIf | ForEach-Object { Write-Verbose "Set $($_.AddressFamily) InterfaceMetric to $using:InterfaceMetric"; + $_ | Set-NetIpInterface -InterfaceMetric $using:InterfaceMetric } + } + GetScript = { return ` + @{ + result = 'N/A' + } + } + } + } + + WinsSetting "LmhostsLookup_$InterfaceAlias" + { + EnableLmHosts = $EnableLmhostsLookup + IsSingleInstance = 'Yes' + } + + if ($DisableNetbios) + { + NetBios "DisableNetBios_$InterfaceAlias" + { + InterfaceAlias = $InterfaceAlias + Setting = 'Disable' + } + } + + if ($DisableIPv6) + { + NetAdapterBinding "DisableIPv6_$InterfaceAlias" + { + InterfaceAlias = $InterfaceAlias + ComponentId = 'ms_tcpip6' + State = 'Disabled' + } + } + + if (-not [string]::IsNullOrWhiteSpace($NetworkCategory)) + { + if (-not ($NetworkCategory -match '^(Public|Private|DomainAuthenticated)$')) + { + throw "ERROR: Invalid value of attribute 'NetworkCategory'." + } + + Script "NetworkCategory_$InterfaceAlias" + { + TestScript = { + $val = Get-NetConnectionProfile -InterfaceAlias $using:InterfaceAlias + + Write-Verbose "Current NetworkCategory of interface '$using:InterfaceAlias': $($val.NetworkCategory)" + + if ($null -ne $val -and $val.NetworkCategory -eq $using:NetworkCategory) + { + return $true + } + Write-Verbose "Values are different (expected NetworkCategory: $using:NetworkCategory)" + return $false + } + SetScript = { + if ($using:NetworkCategory -eq 'DomainAuthenticated') + { + Write-Verbose "Set NetworkCategory of interface '$using:InterfaceAlias' to '$using:NetworkCategory ' is not supported. The computer automatically sets this value when the network is authenticated to a domain controller." + + # Workaround if the computer is domain joined -> Restart NLA service to restart the network location check + # see https://newsignature.com/articles/network-location-awareness-service-can-ruin-day-fix/ + Write-Verbose "Restarting NLA service to reinitialize the network location check..." + Restart-Service nlasvc -Force + Start-Sleep 5 + + $val = Get-NetConnectionProfile -InterfaceAlias $using:InterfaceAlias + + Write-Verbose "Current NetworkCategory is now: $($val.NetworkCategory)" + + if ($val.NetworkCategory -ne $using:NetworkCategory) + { + Write-Error "Interface '$using:InterfaceAlias' is not '$using:NetworkCategory'." + } + } + else + { + Write-Verbose "Set NetworkCategory of interface '$using:InterfaceAlias' to '$using:NetworkCategory '." + Set-NetConnectionProfile -InterfaceAlias $using:InterfaceAlias -NetworkCategory $using:NetworkCategory + } + } + GetScript = { return ` + @{ + result = 'N/A' + } + } + } + } + } + + if ($DisableNetbios -eq $true) + { + NetBios DisableNetBios_System + { + InterfaceAlias = '*' + Setting = 'Disable' + } + } + + if ($ConfigureIPv6 -ge 0) + { + # see https://docs.microsoft.com/en-US/troubleshoot/windows-server/networking/configure-ipv6-in-windows + + if ($ConfigureIPv6 -gt 255) + { + throw "ERROR: Invalid IPv6 configuration value $ConfigureIPv6 (expected value: 0-255)." + } + + $configIPv6KeyName = "HKLM:\SYSTEM\CurrentControlSet\Services\Tcpip6\Parameters" + $configIPv6VarName = 'DisabledComponents' + + Script ConfigureIPv6_System + { + TestScript = { + $val = Get-ItemProperty -Path $using:configIPv6KeyName -Name $using:configIPv6VarName -ErrorAction SilentlyContinue + + Write-Verbose "Current IPv6 Configuration value: '$($val.$using:configIPv6VarName)' - expected value: '$using:ConfigureIPv6'" + + if ($null -ne $val -and $val.$using:configIPv6VarName -eq $using:ConfigureIPv6) + { + return $true + } + Write-Verbose 'Values are different' + return $false + } + + SetScript = { + if ( -not (Test-Path -Path $using:configIPv6KeyName) ) + { + New-Item -Path $using:configIPv6KeyName -Force + } + Set-ItemProperty -Path $using:configIPv6KeyName -Name $using:configIPv6VarName -Value $using:ConfigureIPv6 -Type DWord + $global:DSCMachineStatus = 1 + } + + GetScript = { return ` + @{ + result = 'N/A' + } + } + } + } + + if ($null -ne $Interfaces) + { + foreach ( $netIf in $Interfaces ) + { + # Remove case sensitivity of ordered Dictionary or Hashtables + $netIf = @{} + $netIf + + if ( [string]::IsNullOrWhitespace($netIf.InterfaceAlias) ) + { + $netIf.InterfaceAlias = 'Ethernet' + } + if ( $DisableNetbios -eq $true -or [string]::IsNullOrWhitespace($netIf.DisableNetbios) ) + { + $netIf.DisableNetbios = $false + } + if ( [string]::IsNullOrWhitespace($netIf.EnableLmhostsLookup) ) + { + $netIf.EnableLmhostsLookup = $false + } + if ( [string]::IsNullOrWhitespace($netIf.EnableDhcp) ) + { + $netIf.EnableDhcp = $false + } + if ( $DisableIPv6 -eq $true -or [string]::IsNullOrWhitespace($netIf.DisableIPv6) ) + { + $netIf.DisableIPv6 = $false + } + if ( $netIf.EnableDhcp -eq $true -and [string]::IsNullOrWhitespace($netIf.Prefix) ) + { + $netIf.Prefix = 24 + } + + NetIpInterfaceConfig @netIf + } + } + + if ($null -ne $Routes) + { + foreach ( $netRoute in $Routes ) + { + # Remove case sensitivity of ordered Dictionary or Hashtables + $netRoute = @{} + $netRoute + + if ( [string]::IsNullOrWhitespace($netRoute.InterfaceAlias) ) + { + $netRoute.InterfaceAlias = 'Ethernet' + } + + if ( [string]::IsNullOrWhitespace($netRoute.AddressFamily) ) + { + $netRoute.AddressFamily = 'IPv4' + } + + if ( [string]::IsNullOrWhitespace($netRoute.Ensure) ) + { + $netRoute.Ensure = 'Present' + } + + $executionName = "route_$($netRoute.InterfaceAlias)_$($netRoute.AddressFamily)_$($netRoute.DestinationPrefix)_$($netRoute.NextHop)" -replace '[().:\s]', '' + (Get-DscSplattedResource -ResourceName Route -ExecutionName $executionName -Properties $netRoute -NoInvoke).Invoke($netRoute) + } + } +} diff --git a/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/RegistryValues/RegistryValues.psd1 b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/RegistryValues/RegistryValues.psd1 new file mode 100644 index 00000000..54e20895 --- /dev/null +++ b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/RegistryValues/RegistryValues.psd1 @@ -0,0 +1,15 @@ +@{ + RootModule = 'RegistryValues.schema.psm1' + + ModuleVersion = '0.0.1' + + GUID = '2aab897b-85c4-4643-8928-67892348dd1a' + + Author = 'NA' + + CompanyName = 'NA' + + Copyright = 'NA' + + DscResourcesToExport = @('RegistryValues') +} diff --git a/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/RegistryValues/RegistryValues.schema.psm1 b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/RegistryValues/RegistryValues.schema.psm1 new file mode 100644 index 00000000..15816a07 --- /dev/null +++ b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/RegistryValues/RegistryValues.schema.psm1 @@ -0,0 +1,28 @@ +configuration RegistryValues { + param ( + [Parameter(Mandatory = $true)] + [hashtable[]] + $Values + ) + + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName xPSDesiredStateConfiguration + + foreach ($value in $values) + { + if (-not $value.ContainsKey('Ensure')) + { + $value.Ensure = 'Present' + } + if ([String]::IsNullOrEmpty($value.ValueName)) + { + $value.ValueName = '' + } + if ([String]::IsNullOrEmpty($value.ValueData) -and ($value.Ensure -eq 'Present')) + { + $value.ValueData = '' + } + $executionName = ($value.Key + '__' + $value.ValueName) -replace '[\s(){}/\\:-]', '_' + (Get-DscSplattedResource -ResourceName xRegistry -ExecutionName $executionName -Properties $value -NoInvoke).Invoke($value) + } +} diff --git a/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/Scripts/Scripts.psd1 b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/Scripts/Scripts.psd1 new file mode 100644 index 00000000..f3c4e92e --- /dev/null +++ b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/Scripts/Scripts.psd1 @@ -0,0 +1,15 @@ +@{ + RootModule = 'Scripts.schema.psm1' + + ModuleVersion = '0.0.1' + + GUID = '58f0ca1a-02df-42ea-84b3-c7ff3067fad2' + + Author = 'NA' + + CompanyName = 'NA' + + Copyright = 'NA' + + DscResourcesToExport = @('Scripts') +} diff --git a/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/Scripts/Scripts.schema.psm1 b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/Scripts/Scripts.schema.psm1 new file mode 100644 index 00000000..6c32944f --- /dev/null +++ b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/Scripts/Scripts.schema.psm1 @@ -0,0 +1,51 @@ +configuration Scripts { + param ( + [Parameter(Mandatory = $true)] + [hashtable[]] + $Items + ) + + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName xPSDesiredStateConfiguration + + foreach ($item in $Items) + { + # remove case sensivity from hashtable + $item = @{} + $item + + if ($null -ne $item.Params) + { + $params = '$params = ''' + ($item.Params | ConvertTo-Json -Compress) + "' | ConvertFrom-JSON;`n" + } + + if ([string]::IsNullOrWhiteSpace($item.GetScript)) + { + $item.GetScript = "@{ Result = 'N/A' }" + } + elseif ($null -ne $params) + { + $item.GetScript = $params + $item.GetScript + } + + if ([string]::IsNullOrWhiteSpace($item.SetScript)) + { + $item.SetScript = "Write-Error 'SetScript is not implemented.'" + } + elseif ($null -ne $params) + { + $item.SetScript = $params + $item.SetScript + } + + if ($null -ne $params) + { + $item.TestScript = $params + $item.TestScript + } + + $executionName = "Script_$($item.Name)" + + [void]$item.Remove('Name') + [void]$item.Remove('Params') + + (Get-DscSplattedResource -ResourceName xScript -ExecutionName $executionName -Properties $item -NoInvoke).Invoke($item) + } +} diff --git a/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/SecurityBase/SecurityBase.psd1 b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/SecurityBase/SecurityBase.psd1 new file mode 100644 index 00000000..8d62ad37 --- /dev/null +++ b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/SecurityBase/SecurityBase.psd1 @@ -0,0 +1,15 @@ +@{ + RootModule = 'SecurityBase.schema.psm1' + + ModuleVersion = '0.0.1' + + GUID = '6b2f1809-107e-474b-84fa-d0a658ef4285' + + Author = 'NA' + + CompanyName = 'NA' + + Copyright = 'NA' + + DscResourcesToExport = @('SecurityBase') +} diff --git a/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/SecurityBase/SecurityBase.schema.psm1 b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/SecurityBase/SecurityBase.schema.psm1 new file mode 100644 index 00000000..58588cbc --- /dev/null +++ b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/SecurityBase/SecurityBase.schema.psm1 @@ -0,0 +1,64 @@ +configuration SecurityBase { + param ( + [Parameter()] + [ValidateSet('Baseline', 'WebServer', 'FileServer')] + [string] + $Role + ) + + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName xPSDesiredStateConfiguration + Import-DscResource -ModuleName ComputerManagementDsc + Import-DscResource -ModuleName SecurityPolicyDsc + + #Baseline + xWindowsFeature DisableSmbV1 + { + Name = 'FS-SMB1' + Ensure = 'Absent' + } + + PowerShellExecutionPolicy ExecutionPolicyAllSigned + { + ExecutionPolicyScope = 'LocalMachine' + ExecutionPolicy = 'RemoteSigned' + } + + UserRightsAssignment DenyLogonLocallyForAdministrator + { + Policy = 'Deny_log_on_locally' + Identity = 'contoso\Administrator' + } + + UserRightsAssignment AllowLogonLocally + { + Policy = 'Allow_log_on_locally' + Identity = 'Administrators', 'Backup Operators' + } + + #FileServer + if ($Role -eq 'FileServer') + { + SecurityOption SecOptionsFileServer + { + Name = 'Web Server Secutiry options' + Interactive_logon_Message_title_for_users_attempting_to_log_on = 'Secure File Server' + Interactive_logon_Message_text_for_users_attempting_to_log_on = 'Your are logging on to a secure file server' + Accounts_Rename_administrator_account = 'a' + } + } + + #Web Server + if ($Role -eq 'WebServer') + { + SecurityOption SecOptionsWebServer + { + Name = 'Web Server Secutiry options' + Interactive_logon_Message_title_for_users_attempting_to_log_on = 'Secure Web Server' + Interactive_logon_Message_text_for_users_attempting_to_log_on = 'Your are logging on to a secure web server' + Accounts_Rename_administrator_account = 'a' + Network_security_LAN_Manager_authentication_level = 'Send NTLMv2 responses only. Refuse LM & NTLM' + Network_security_Do_not_store_LAN_Manager_hash_value_on_next_password_change = 'Enabled' + } + } +} diff --git a/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/SmbShares/SmbShares.psd1 b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/SmbShares/SmbShares.psd1 new file mode 100644 index 00000000..548addd4 --- /dev/null +++ b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/SmbShares/SmbShares.psd1 @@ -0,0 +1,15 @@ +@{ + RootModule = 'SmbShares.schema.psm1' + + ModuleVersion = '0.0.1' + + GUID = 'bef9ac33-d589-44ea-844e-11ff75ffe7df' + + Author = 'NA' + + CompanyName = 'NA' + + Copyright = 'NA' + + DscResourcesToExport = @('SmbShares') +} diff --git a/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/SmbShares/SmbShares.schema.psm1 b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/SmbShares/SmbShares.schema.psm1 new file mode 100644 index 00000000..589c4c5f --- /dev/null +++ b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/SmbShares/SmbShares.schema.psm1 @@ -0,0 +1,109 @@ +configuration SmbShares +{ + param ( + [Parameter()] + [ValidateSet('Server', 'Client')] + $HostOS = 'Server', + + [Parameter()] + [hashtable] + $ServerConfiguration, + + [Parameter()] + [hashtable[]] + $Shares + ) + + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName ComputerManagementDsc + + if ($HostOS -eq 'Server') + { + WindowsFeature featureFileServer + { + Name = 'FS-FileServer' + Ensure = 'Present' + } + + $featureFileServer = '[WindowsFeature]featureFileServer' + } + + if ($null -ne $ServerConfiguration) + { + if ($HostOS -eq 'Server') + { + if ($ServerConfiguration.EnableSMB1Protocol -eq $false) + { + WindowsFeature removeSMB1 + { + Name = 'FS-SMB1' + Ensure = 'Absent' + DependsOn = $featureFileServer + } + } + + $ServerConfiguration.DependsOn = $featureFileServer + } + + $ServerConfiguration.IsSingleInstance = 'Yes' + + (Get-DscSplattedResource -ResourceName SmbServerConfiguration -ExecutionName 'smbServerConfig' -Properties $ServerConfiguration -NoInvoke).Invoke($ServerConfiguration) + } + + if ($null -ne $Shares) + { + foreach ($share in $Shares) + { + # Remove Case Sensitivity of ordered Dictionary or Hashtables + $share = @{} + $share + + $shareId = $share.Name -replace '[:$\s]', '_' + + $share.DependsOn = $featureFileServer + + if (-not $share.ContainsKey('Ensure')) + { + $share.Ensure = 'Present' + } + + if ($share.Ensure -eq 'Present') + { + if ([string]::IsNullOrWhiteSpace($share.Path)) + { + throw "ERROR: Missing path of the SMB share '$($share.Name)'." + } + + # skip root paths + $dirInfo = New-Object -TypeName System.IO.DirectoryInfo -ArgumentList $share.Path + + if ($null -ne $dirInfo.Parent) + { + File "Folder_$shareId" + { + DestinationPath = $share.Path + Type = 'Directory' + Ensure = 'Present' + DependsOn = $featureFileServer + } + + $share.DependsOn = "[File]Folder_$shareId" + } + } + elseif ([string]::IsNullOrWhiteSpace($share.Path)) + { + $share.Path = 'Unused' + } + + # remove duplicates from access rights + $share.FullAccess = $() + $share.FullAccess + $share.ChangeAccess = $() + ($share.ChangeAccess | Where-Object { $share.FullAccess -notcontains $_ }) + $share.ReadAccess = $() + ($share.ReadAccess | Where-Object { $share.FullAccess -notcontains $_ -and ` + $share.ChangeAccess -notcontains $_ }) + $share.NoAccess = $() + ($share.NoAccess | Where-Object { $share.FullAccess -notcontains $_ -and ` + $share.ChangeAccess -notcontains $_ -and ` + $share.ReadAccess -notcontains $_ }) + + (Get-DscSplattedResource -ResourceName SmbShare -ExecutionName "SmbShare_$shareId" -Properties $share -NoInvoke).Invoke($share) + } + } +} diff --git a/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/SoftwarePackages/SoftwarePackages.psd1 b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/SoftwarePackages/SoftwarePackages.psd1 new file mode 100644 index 00000000..e4b133ec --- /dev/null +++ b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/SoftwarePackages/SoftwarePackages.psd1 @@ -0,0 +1,15 @@ +@{ + RootModule = 'SoftwarePackages.schema.psm1' + + ModuleVersion = '0.0.1' + + GUID = '0245e22a-4b66-44e6-ba2b-1aa8df37ef67' + + Author = 'NA' + + CompanyName = 'NA' + + Copyright = 'NA' + + DscResourcesToExport = @('SoftwarePackages') +} diff --git a/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/SoftwarePackages/SoftwarePackages.schema.psm1 b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/SoftwarePackages/SoftwarePackages.schema.psm1 new file mode 100644 index 00000000..df698d4a --- /dev/null +++ b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/SoftwarePackages/SoftwarePackages.schema.psm1 @@ -0,0 +1,21 @@ +configuration SoftwarePackages { + param ( + [Parameter(Mandatory = $true)] + [hashtable[]]$Packages + ) + + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName xPSDesiredStateConfiguration + + foreach ($p in $Packages) + { + $p.Ensure = 'Present' + if (-not $p.ProductId) + { + $p.ProductId = '' + } + + $executionName = $p.Name -replace '\(|\)|\.| ', '' + (Get-DscSplattedResource -ResourceName xPackage -ExecutionName $executionName -Properties $p -NoInvoke).Invoke($p) + } +} diff --git a/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/WebApplicationPools/WebApplicationPools.psd1 b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/WebApplicationPools/WebApplicationPools.psd1 new file mode 100644 index 00000000..6fb5f1bf --- /dev/null +++ b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/WebApplicationPools/WebApplicationPools.psd1 @@ -0,0 +1,15 @@ +@{ + RootModule = 'WebApplicationPools.schema.psm1' + + ModuleVersion = '0.0.1' + + GUID = '7add3852-42b4-4e43-a7b2-4c460608b2c1' + + Author = 'NA' + + CompanyName = 'NA' + + Copyright = 'NA' + + DscResourcesToExport = @('WebApplicationPools') +} diff --git a/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/WebApplicationPools/WebApplicationPools.schema.psm1 b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/WebApplicationPools/WebApplicationPools.schema.psm1 new file mode 100644 index 00000000..fae55946 --- /dev/null +++ b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/WebApplicationPools/WebApplicationPools.schema.psm1 @@ -0,0 +1,21 @@ +configuration WebApplicationPools { + param ( + [Parameter(Mandatory = $true)] + [hashtable[]] + $Items + ) + + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName WebAdministrationDsc + + foreach ($item in $Items) + { + if (-not $item.ContainsKey('Ensure')) + { + $item.Ensure = 'Present' + } + + $executionName = $item.Name + (Get-DscSplattedResource -ResourceName WebAppPool -ExecutionName $executionName -Properties $item -NoInvoke).Invoke($item) + } +} diff --git a/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/WebApplications/WebApplications.psd1 b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/WebApplications/WebApplications.psd1 new file mode 100644 index 00000000..34b81f46 --- /dev/null +++ b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/WebApplications/WebApplications.psd1 @@ -0,0 +1,15 @@ +@{ + RootModule = 'WebApplications.schema.psm1' + + ModuleVersion = '0.0.1' + + GUID = '870e34ff-7351-4348-bc79-716466619e07' + + Author = 'NA' + + CompanyName = 'NA' + + Copyright = 'NA' + + DscResourcesToExport = @('WebApplications') +} diff --git a/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/WebApplications/WebApplications.schema.psm1 b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/WebApplications/WebApplications.schema.psm1 new file mode 100644 index 00000000..6b9d70d6 --- /dev/null +++ b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/WebApplications/WebApplications.schema.psm1 @@ -0,0 +1,32 @@ +configuration WebApplications { + param ( + [Parameter(Mandatory = $true)] + [hashtable[]] + $Items + ) + + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName WebAdministrationDsc + + $dscResourceName = 'WebApplication' + + foreach ($item in $Items) + { + # Remove Case Sensitivity of ordered Dictionary or Hashtables + $item = @{} + $item + + if (-not $item.ContainsKey('Ensure')) + { + $item.Ensure = 'Present' + } + + $executionName = "webapp_$($item.Name -replace '[{}#\-\s]','_')" + + if ($item.AuthenticationInfo) + { + $item.AuthenticationInfo = (Get-DscSplattedResource -ResourceName DSC_WebApplicationAuthenticationInformation -Properties $item.AuthenticationInfo -NoInvoke).Invoke($item.AuthenticationInfo) + } + + (Get-DscSplattedResource -ResourceName $dscResourceName -ExecutionName $executionName -Properties $item -NoInvoke).Invoke($item) + } +} diff --git a/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/WebConfigProperties/WebConfigProperties.psd1 b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/WebConfigProperties/WebConfigProperties.psd1 new file mode 100644 index 00000000..dd321d52 --- /dev/null +++ b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/WebConfigProperties/WebConfigProperties.psd1 @@ -0,0 +1,15 @@ +@{ + RootModule = 'WebConfigProperties.schema.psm1' + + ModuleVersion = '0.0.1' + + GUID = '19661b84-a84b-4275-8065-2c41de1bf8a9' + + Author = 'NA' + + CompanyName = 'NA' + + Copyright = 'NA' + + DscResourcesToExport = @('WebConfigProperties') +} diff --git a/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/WebConfigProperties/WebConfigProperties.schema.psm1 b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/WebConfigProperties/WebConfigProperties.schema.psm1 new file mode 100644 index 00000000..c201dbb2 --- /dev/null +++ b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/WebConfigProperties/WebConfigProperties.schema.psm1 @@ -0,0 +1,31 @@ +configuration WebConfigProperties { + param ( + [Parameter(Mandatory = $true)] + [hashtable[]] + $Items + ) + + <# + Filter = [string] + PropertyName = [string] + WebsitePath = [string] + [DependsOn = [string[]]] + [Ensure = [string]{ Absent | Present }] + [PsDscRunAsCredential = [PSCredential]] + [Value = [string]] + #> + + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName WebAdministrationDsc + + foreach ($item in $Items) + { + if (-not $item.ContainsKey('Ensure')) + { + $item.Ensure = 'Present' + } + + $executionName = "$($item.WebsitePath)_$($item.Filter)_$($item.PropertyName)" -replace '[\s(){}/\\:-]', '_' + (Get-DscSplattedResource -ResourceName WebConfigProperty -ExecutionName $executionName -Properties $item -NoInvoke).Invoke($item) + } +} diff --git a/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/WebConfigPropertyCollections/WebConfigPropertyCollections.psd1 b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/WebConfigPropertyCollections/WebConfigPropertyCollections.psd1 new file mode 100644 index 00000000..dafafdca --- /dev/null +++ b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/WebConfigPropertyCollections/WebConfigPropertyCollections.psd1 @@ -0,0 +1,15 @@ +@{ + RootModule = 'WebConfigPropertyCollections.schema.psm1' + + ModuleVersion = '0.0.1' + + GUID = '315986ab-9f7d-4d70-baf8-a7b8dd06b5f5' + + Author = 'NA' + + CompanyName = 'NA' + + Copyright = 'NA' + + DscResourcesToExport = @('WebConfigPropertyCollections') +} diff --git a/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/WebConfigPropertyCollections/WebConfigPropertyCollections.schema.psm1 b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/WebConfigPropertyCollections/WebConfigPropertyCollections.schema.psm1 new file mode 100644 index 00000000..a538437d --- /dev/null +++ b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/WebConfigPropertyCollections/WebConfigPropertyCollections.schema.psm1 @@ -0,0 +1,35 @@ +configuration WebConfigPropertyCollections { + param ( + [Parameter(Mandatory = $true)] + [hashtable[]] + $Items + ) + + <# + CollectionName = [string] + Filter = [string] + ItemKeyName = [string] + ItemKeyValue = [string] + ItemName = [string] + ItemPropertyName = [string] + WebsitePath = [string] + [DependsOn = [string[]]] + [Ensure = [string]{ Absent | Present }] + [ItemPropertyValue = [string]] + [PsDscRunAsCredential = [PSCredential]] + #> + + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName WebAdministrationDsc + + foreach ($item in $Items) + { + if (-not $item.ContainsKey('Ensure')) + { + $item.Ensure = 'Present' + } + + $executionName = "$($item.WebsitePath)_$($item.Filter)_$($item.CollectionName)_$($item.ItemKeyValue)_$($item.ItemPropertyName)" -replace '[\s(){}/\\:-]', '_' + (Get-DscSplattedResource -ResourceName WebConfigPropertyCollection -ExecutionName $executionName -Properties $item -NoInvoke).Invoke($item) + } +} diff --git a/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/WebSites/Websites.psd1 b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/WebSites/Websites.psd1 new file mode 100644 index 00000000..67c6c441 --- /dev/null +++ b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/WebSites/Websites.psd1 @@ -0,0 +1,15 @@ +@{ + RootModule = 'WebSites.schema.psm1' + + ModuleVersion = '0.0.1' + + GUID = '1a2db293-0d41-4062-9668-00e6e71d6436' + + Author = 'NA' + + CompanyName = 'NA' + + Copyright = 'NA' + + DscResourcesToExport = @('WebSites') +} diff --git a/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/WebSites/Websites.schema.psm1 b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/WebSites/Websites.schema.psm1 new file mode 100644 index 00000000..65c19df0 --- /dev/null +++ b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/WebSites/Websites.schema.psm1 @@ -0,0 +1,40 @@ +configuration WebSites { + param ( + [Parameter(Mandatory = $true)] + [hashtable[]] + $Items + ) + + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName WebAdministrationDsc + + $dscResourceName = 'WebSite' + + foreach ($item in $Items) + { + # Remove Case Sensitivity of ordered Dictionary or Hashtables + $item = @{} + $item + + if (-not $item.ContainsKey('Ensure')) + { + $item.Ensure = 'Present' + } + + if ($item.BindingINfo) + { + $dscBindingInfos = foreach ($bindingInfo in $item.BindingInfo) + { + (Get-DscSplattedResource -ResourceName DSC_WebBindingInformation -Properties $bindingInfo -NoInvoke).Invoke($bindingInfo) + } + $item.BindingInfo = $dscBindingInfos + } + + if ($item.AuthenticationInfo) + { + $item.AuthenticationInfo = (Get-DscSplattedResource -ResourceName DSC_WebAuthenticationInformation -Properties $item.AuthenticationInfo -NoInvoke).Invoke($item.AuthenticationInfo) + } + + $executionName = "website_$($item.Name -replace '[{}#\-\s]','_')" + (Get-DscSplattedResource -ResourceName $dscResourceName -ExecutionName $executionName -Properties $item -NoInvoke).Invoke($item) + } +} diff --git a/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/WebVirtualDirectories/WebVirtualDirectories.psd1 b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/WebVirtualDirectories/WebVirtualDirectories.psd1 new file mode 100644 index 00000000..bcad092c --- /dev/null +++ b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/WebVirtualDirectories/WebVirtualDirectories.psd1 @@ -0,0 +1,15 @@ +@{ + RootModule = 'WebVirtualDirectories.schema.psm1' + + ModuleVersion = '0.0.1' + + GUID = 'c6bf239b-fe92-41d8-b045-2ec3b28b2216' + + Author = 'NA' + + CompanyName = 'NA' + + Copyright = 'NA' + + DscResourcesToExport = @('WebVirtualDirectories') +} diff --git a/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/WebVirtualDirectories/WebVirtualDirectories.schema.psm1 b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/WebVirtualDirectories/WebVirtualDirectories.schema.psm1 new file mode 100644 index 00000000..71e46680 --- /dev/null +++ b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/WebVirtualDirectories/WebVirtualDirectories.schema.psm1 @@ -0,0 +1,21 @@ +configuration WebVirtualDirectories { + param ( + [Parameter(Mandatory = $true)] + [hashtable[]] + $Items + ) + + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName WebAdministrationDsc + + foreach ($item in $Items) + { + if (-not $item.ContainsKey('Ensure')) + { + $item.Ensure = 'Present' + } + + $executionName = $item.Name + (Get-DscSplattedResource -ResourceName WebVirtualDirectory -ExecutionName $executionName -Properties $item -NoInvoke).Invoke($item) + } +} diff --git a/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/WindowsEventLogs/WindowsEventLogs.psd1 b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/WindowsEventLogs/WindowsEventLogs.psd1 new file mode 100644 index 00000000..5562b057 --- /dev/null +++ b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/WindowsEventLogs/WindowsEventLogs.psd1 @@ -0,0 +1,9 @@ +@{ + RootModule = 'WindowsEventLogs.schema.psm1' + ModuleVersion = '0.0.1' + GUID = '3ef0e3c4-2399-48a9-a0bf-cc63bae7a9ef' + Author = 'NA' + CompanyName = 'NA' + Copyright = 'NA' + DscResourcesToExport = @('WindowsEventLogs') +} diff --git a/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/WindowsEventLogs/WindowsEventLogs.schema.psm1 b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/WindowsEventLogs/WindowsEventLogs.schema.psm1 new file mode 100644 index 00000000..c4f95225 --- /dev/null +++ b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/WindowsEventLogs/WindowsEventLogs.schema.psm1 @@ -0,0 +1,24 @@ +configuration WindowsEventLogs { + param ( + [Parameter(Mandatory = $true)] + [hashtable[]] + $Logs + ) + + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName ComputerManagementDsc + + foreach ($log in $Logs) + { + # convert string with KB/MB/GB to Sint64 + if ($null -ne $log.MaximumSizeInBytes) + { + $log.MaximumSizeInBytes = [System.Int64] ($log.MaximumSizeInBytes / 1) + } + + $executionName = "$($log.LogName)" -replace ' ', '' + (Get-DscSplattedResource -ResourceName WindowsEventLog -ExecutionName $executionName -Properties $log -NoInvoke).Invoke($log) + + } + +} diff --git a/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/WindowsFeatures/WindowsFeatures.psd1 b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/WindowsFeatures/WindowsFeatures.psd1 new file mode 100644 index 00000000..e7b8927d --- /dev/null +++ b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/WindowsFeatures/WindowsFeatures.psd1 @@ -0,0 +1,15 @@ +@{ + RootModule = 'WindowsFeatures.schema.psm1' + + ModuleVersion = '0.0.1' + + GUID = 'cb8a795a-b1c1-468d-a841-a24c50c0611b' + + Author = 'NA' + + CompanyName = 'NA' + + Copyright = 'NA' + + DscResourcesToExport = @('WindowsFeatures') +} diff --git a/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/WindowsFeatures/WindowsFeatures.schema.psm1 b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/WindowsFeatures/WindowsFeatures.schema.psm1 new file mode 100644 index 00000000..58d198bf --- /dev/null +++ b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/WindowsFeatures/WindowsFeatures.schema.psm1 @@ -0,0 +1,80 @@ +configuration WindowsFeatures { + param ( + [Parameter()] + [string[]] + $Names, + + [Parameter()] + [hashtable[]] + $Features, + + [Parameter()] + [bool]$UseLegacyResource = $false + ) + + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName xPSDesiredStateConfiguration + + $resourceName = if ($UseLegacyResource) + { + 'WindowsFeature' + } + else + { + 'xWindowsFeature' + } + + foreach ($n in $Names) + { + $ensure = 'Present' + $includeAllSubFeature = $false + + if ($n[0] -in '-', '+', '*') + { + if ($n[0] -eq '-') + { + $ensure = 'Absent' + } + elseif ($n[0] -eq '*') + { + $includeAllSubFeature = $true + } + $n = $n.Substring(1) + } + + $params = @{ + Name = $n + Ensure = $ensure + IncludeAllSubFeature = $includeAllSubFeature + } + + (Get-DscSplattedResource -ResourceName $resourceName -ExecutionName $params.Name -Properties $params -NoInvoke).Invoke($params) + } + + <# + @{ + Name = [string] + [Credential = [PSCredential]] + [DependsOn = [string[]]] + [Ensure = [string]{ Absent | Present }] + [IncludeAllSubFeature = [bool]] + [LogPath = [string]] + [PsDscRunAsCredential = [PSCredential]] + [Source = [string]] +} + #> + foreach ($feature in $Features) + { + $resourceName = if ($feature.UseLegacyResource) + { + 'WindowsFeature' + } + else + { + 'xWindowsFeature' + } + $feature.remove('UseLegacyResource') + + (Get-DscSplattedResource -ResourceName $resourceName -ExecutionName $feature.Name -Properties $feature -NoInvoke).Invoke($feature) + } +} diff --git a/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/WindowsOptionalFeatures/WindowsOptionalFeatures.psd1 b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/WindowsOptionalFeatures/WindowsOptionalFeatures.psd1 new file mode 100644 index 00000000..664a72c1 --- /dev/null +++ b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/WindowsOptionalFeatures/WindowsOptionalFeatures.psd1 @@ -0,0 +1,15 @@ +@{ + RootModule = 'WindowsOptionalFeatures.schema.psm1' + + ModuleVersion = '0.0.1' + + GUID = 'a5a89c1d-c2b9-4e92-be10-31fad332fe0a' + + Author = 'NA' + + CompanyName = 'NA' + + Copyright = 'NA' + + DscResourcesToExport = @('WindowsOptionalFeatures') +} diff --git a/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/WindowsOptionalFeatures/WindowsOptionalFeatures.schema.psm1 b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/WindowsOptionalFeatures/WindowsOptionalFeatures.schema.psm1 new file mode 100644 index 00000000..e2f10f2f --- /dev/null +++ b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/WindowsOptionalFeatures/WindowsOptionalFeatures.schema.psm1 @@ -0,0 +1,40 @@ +configuration WindowsOptionalFeatures { + param ( + [Parameter(Mandatory = $true)] + [string[]] + $Names, + + [Parameter()] + [boolean] + $RemoveFilesOnDisable = $false, + + [Parameter()] + [boolean] + $NoWindowsUpdateCheck = $false + ) + + Import-DscResource -ModuleName PSDesiredStateConfiguration + + foreach ($n in $Names) + { + $ensure = 'Enable' + + if ($n[0] -in '-', '+') + { + if ($n[0] -eq '-') + { + $ensure = 'Disable' + } + $n = $n.Substring(1) + } + + $params = @{ + Name = $n + Ensure = $ensure + RemoveFilesOnDisable = $RemoveFilesOnDisable + NoWindowsUpdateCheck = $NoWindowsUpdateCheck + } + + (Get-DscSplattedResource -ResourceName WindowsOptionalFeature -ExecutionName $params.Name -Properties $params -NoInvoke).Invoke($params) + } +} diff --git a/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/WindowsServices/WindowsServices.psd1 b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/WindowsServices/WindowsServices.psd1 new file mode 100644 index 00000000..1e0e7243 --- /dev/null +++ b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/WindowsServices/WindowsServices.psd1 @@ -0,0 +1,15 @@ +@{ + RootModule = 'WindowsServices.schema.psm1' + + ModuleVersion = '0.0.1' + + GUID = '06f7eb99-05a9-4476-8530-e4d28030fe70' + + Author = 'NA' + + CompanyName = 'NA' + + Copyright = 'NA' + + DscResourcesToExport = @('WindowsServices') +} diff --git a/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/WindowsServices/WindowsServices.schema.psm1 b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/WindowsServices/WindowsServices.schema.psm1 new file mode 100644 index 00000000..3adf217f --- /dev/null +++ b/output/RequiredModules/DscConfig.Demo/0.8.3/DSCResources/WindowsServices/WindowsServices.schema.psm1 @@ -0,0 +1,81 @@ +configuration WindowsServices { + param ( + [Parameter(Mandatory = $true)] + [hashtable[]] + $Services + ) + + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName xPSDesiredStateConfiguration + + foreach ($service in $Services) + { + # Remove Case Sensitivity of ordered Dictionary or Hashtables + $service = @{} + $service + + $service.Ensure = 'Present' + + [boolean]$delayedStart = $false + + # additional Support for delayed start + if ($service.StartupType -eq 'AutomaticDelayedStart') + { + $service.StartupType = 'Automatic' + $delayedStart = $true + } + + # set defaults if no state is specified + if ([string]::IsNullOrWhiteSpace($service.State)) + { + # check for running service only if none or a compatible startup type is specified + if ([string]::IsNullOrWhiteSpace($service.StartupType) -or ($service.StartupType -eq 'Automatic')) + { + $service.State = 'Running' + } + elseif ($service.StartupType -eq 'Disabled') + { + $service.State = 'Stopped' + } + else + { + $service.State = 'Ignore' + } + } + + $executionName = "winsvc_$($Service.Name -replace '[-().:$#\s]', '_')" + + #how splatting of DSC resources works: https://gaelcolas.com/2017/11/05/pseudo-splatting-dsc-resources/ + (Get-DscSplattedResource -ResourceName xService -ExecutionName $executionName -Properties $service -NoInvoke).Invoke($service) + + if ($delayedStart -eq $true) + { + $serviceName = $Service.Name + + Script "$($executionName)_delayedstart" + { + TestScript = { + $key = "HKLM:SYSTEM\CurrentControlSet\Services\$using:serviceName" + $val = Get-ItemProperty -Path $key -Name 'DelayedAutostart' -ErrorAction SilentlyContinue + + Write-Verbose "Read DelayedAutostart at $($key): $(if( $null -eq $val.DelayedAutostart ) { 'not found' } else { $val.DelayedAutostart })" + if ($null -ne $val.DelayedAutostart -and $val.DelayedAutostart -gt 0 ) + { + return $true + } + return $false + } + SetScript = { + $key = "HKLM:SYSTEM\CurrentControlSet\Services\$using:serviceName" + Write-Verbose "Set DelayedAutostart at $($key) to 1" + Set-ItemProperty -Path $key -Name 'DelayedAutostart' -Value 1 -Type DWord + } + GetScript = { return ` + @{ + result = 'N/A' + } + } + DependsOn = "[xService]$executionName" + } + } + } +} diff --git a/output/RequiredModules/DscConfig.Demo/0.8.3/DscConfig.Demo.psd1 b/output/RequiredModules/DscConfig.Demo/0.8.3/DscConfig.Demo.psd1 new file mode 100644 index 00000000..c87bf4a4 --- /dev/null +++ b/output/RequiredModules/DscConfig.Demo/0.8.3/DscConfig.Demo.psd1 @@ -0,0 +1,36 @@ +@{ + RootModule = 'DscConfig.Demo.psm1' + ModuleVersion = '0.8.3' + GUID = '63e8bf79-62d3-4249-8fe6-9a766fbe8481' + Author = 'DSC Community' + CompanyName = 'DSC Community' + Copyright = 'Copyright the DSC Community contributors. All rights reserved.' + Description = 'DSC composite resource for https://github.com/dsccommunity/DscWorkshop' + PowerShellVersion = '5.1' + FunctionsToExport = '*' + CmdletsToExport = '*' + VariablesToExport = '*' + AliasesToExport = '*' + + PrivateData = @{ + + PSData = @{ + Prerelease = '' + Tags = @('DesiredStateConfiguration', 'DSC', 'DSCResource') + LicenseUri = 'https://github.com/dsccommunity/DscConfig.Demo/blob/main/LICENSE' + ProjectUri = 'https://github.com/dsccommunity/DscConfig.Demo' + IconUri = 'https://dsccommunity.org/images/DSC_Logo_300p.png' + ReleaseNotes = '## [0.8.3] - 2023-03-16 + +### Changed + +- These modules have been updated: + - ComputerManagementDsc to ''9.0.0'' + - NetworkingDsc to ''9.0.0'' + - WebAdministrationDsc to ''4.1.0'' +- Added tasks from ''Sampler.AzureDevOpsTasks'' to fix publishing issues in Azure DevOps Server. + +' + } + } +} diff --git a/output/RequiredModules/DscConfig.Demo/0.8.3/DscConfig.Demo.psm1 b/output/RequiredModules/DscConfig.Demo/0.8.3/DscConfig.Demo.psm1 new file mode 100644 index 00000000..483960ec --- /dev/null +++ b/output/RequiredModules/DscConfig.Demo/0.8.3/DscConfig.Demo.psm1 @@ -0,0 +1,39 @@ +<# +##Import Classes +if (Test-Path "$PSScriptRoot\Classes\classes.psd1") { + $ClassLoadOrder = Import-PowerShellDataFile -Path "$PSScriptRoot\Classes\classes.psd1" -ErrorAction SilentlyContinue +} +else { + $ClassLoadOrder = @{ order=@() } + $ClassLoadOrder.order = (get-childItem "$PSScriptRoot\Classes\*" -Filter *.ps1 -ErrorAction SilentlyContinue).BaseName +} + +foreach ($class in $ClassLoadOrder.order) { + $path = '{0}\classes\{1}.ps1' -f $PSScriptRoot, $class + if (Test-Path $path) { + . $path + } +} + +#Get public and private function definition files. +$Public = @( Get-ChildItem -Path $PSScriptRoot\Public\*.ps1 -ErrorAction SilentlyContinue ) +$Private = @( Get-ChildItem -Path $PSScriptRoot\Private\*.ps1 -ErrorAction SilentlyContinue ) + +#Dot source the files +foreach($import in @($Public + $Private)) +{ + try + { + Write-Verbose "Importing $($Import.FullName)" + . $import.fullname + } + catch + { + Write-Error -Message "Failed to import function $($import.fullname): $_" + } +} + +Export-ModuleMember -Function $Public.Basename +#> + + diff --git a/output/RequiredModules/DscConfig.Demo/0.8.3/PSGetModuleInfo.xml b/output/RequiredModules/DscConfig.Demo/0.8.3/PSGetModuleInfo.xml new file mode 100644 index 00000000..371a939a --- /dev/null +++ b/output/RequiredModules/DscConfig.Demo/0.8.3/PSGetModuleInfo.xml @@ -0,0 +1,143 @@ + + + + Microsoft.PowerShell.Commands.PSRepositoryItemInfo + System.Management.Automation.PSCustomObject + System.Object + + + DscConfig.Demo + 0.8.3 + Module + DSC composite resource for https://github.com/dsccommunity/DscWorkshop + DSC Community + Raimund + Copyright the DSC Community contributors. All rights reserved. +
2023-03-16T17:45:37+01:00
+ + + https://github.com/dsccommunity/DscConfig.Demo/blob/main/LICENSE + https://github.com/dsccommunity/DscConfig.Demo + https://dsccommunity.org/images/DSC_Logo_300p.png + + + System.Object[] + System.Array + System.Object + + + DesiredStateConfiguration + DSC + DSCResource + PSModule + + + + + System.Collections.Hashtable + System.Object + + + + RoleCapability + + + + + + + Function + + + + Command + + + + DscResource + + + + ComputerSettings + ConfigurationBase + DscDiagnostic + DscLcmController + DscLcmMaintenanceWindows + DscTagging + EnvironmentVariables + FilesAndFolders + LocalGroups + LocalUsers + NetworkIpConfiguration + RegistryValues + Scripts + SecurityBase + SmbShares + SoftwarePackages + WebApplicationPools + WebApplications + WebConfigProperties + WebConfigPropertyCollections + WebSites + WebVirtualDirectories + WindowsEventLogs + WindowsFeatures + WindowsOptionalFeatures + WindowsServices + + + + + Workflow + + + + Cmdlet + + + + + + ## [0.8.3] - 2023-03-16_x000D__x000A__x000D__x000A_### Changed_x000D__x000A__x000D__x000A_- These modules have been updated:_x000D__x000A_ - ComputerManagementDsc to '9.0.0'_x000D__x000A_ - NetworkingDsc to '9.0.0'_x000D__x000A_ - WebAdministrationDsc to '4.1.0'_x000D__x000A_- Added tasks from 'Sampler.AzureDevOpsTasks' to fix publishing issues in Azure DevOps Server. + + + + + https://www.powershellgallery.com/api/v2 + PSGallery + NuGet + + + System.Management.Automation.PSCustomObject + System.Object + + + Copyright the DSC Community contributors. All rights reserved. + DSC composite resource for https://github.com/dsccommunity/DscWorkshop + False + ## [0.8.3] - 2023-03-16_x000D__x000A__x000D__x000A_### Changed_x000D__x000A__x000D__x000A_- These modules have been updated:_x000D__x000A_ - ComputerManagementDsc to '9.0.0'_x000D__x000A_ - NetworkingDsc to '9.0.0'_x000D__x000A_ - WebAdministrationDsc to '4.1.0'_x000D__x000A_- Added tasks from 'Sampler.AzureDevOpsTasks' to fix publishing issues in Azure DevOps Server. + True + True + 295 + 1906 + 41965 + 3/16/2023 5:45:37 PM +01:00 + 3/16/2023 5:45:37 PM +01:00 + 6/5/2023 3:12:26 PM +02:00 + DesiredStateConfiguration DSC DSCResource PSModule PSDscResource_ComputerSettings PSDscResource_ConfigurationBase PSDscResource_DscDiagnostic PSDscResource_DscLcmController PSDscResource_DscLcmMaintenanceWindows PSDscResource_DscTagging PSDscResource_EnvironmentVariables PSDscResource_FilesAndFolders PSDscResource_LocalGroups PSDscResource_LocalUsers PSDscResource_NetworkIpConfiguration PSDscResource_RegistryValues PSDscResource_Scripts PSDscResource_SecurityBase PSDscResource_SmbShares PSDscResource_SoftwarePackages PSDscResource_WebApplicationPools PSDscResource_WebApplications PSDscResource_WebConfigProperties PSDscResource_WebConfigPropertyCollections PSDscResource_WebSites PSDscResource_WebVirtualDirectories PSDscResource_WindowsEventLogs PSDscResource_WindowsFeatures PSDscResource_WindowsOptionalFeatures PSDscResource_WindowsServices PSIncludes_DscResource + False + 2023-06-05T15:12:26Z + 0.8.3 + DSC Community + false + Module + DscConfig.Demo.nuspec|DSCResources\DscTagging\DscTagging.schema.psm1|DSCResources\Scripts\Scripts.schema.psm1|DSCResources\WebConfigPropertyCollections\WebConfigPropertyCollections.schema.psm1|DscConfig.Demo.psd1|DSCResources\EnvironmentVariables\EnvironmentVariables.psd1|DSCResources\SecurityBase\SecurityBase.psd1|DSCResources\WebSites\Websites.psd1|DscConfig.Demo.psm1|DSCResources\EnvironmentVariables\EnvironmentVariables.schema.psm1|DSCResources\SecurityBase\SecurityBase.schema.psm1|DSCResources\WebSites\Websites.schema.psm1|DSCResources\ComputerSettings\ComputerSettings.psd1|DSCResources\FilesAndFolders\FilesAndFolders.psd1|DSCResources\SmbShares\SmbShares.psd1|DSCResources\WebVirtualDirectories\WebVirtualDirectories.psd1|DSCResources\ComputerSettings\ComputerSettings.schema.psm1|DSCResources\FilesAndFolders\FilesAndFolders.schema.psm1|DSCResources\SmbShares\SmbShares.schema.psm1|DSCResources\WebVirtualDirectories\WebVirtualDirectories.schema.psm1|DSCResources\ConfigurationBase\ConfigurationBase.psd1|DSCResources\LocalGroups\LocalGroups.psd1|DSCResources\SoftwarePackages\SoftwarePackages.psd1|DSCResources\WindowsEventLogs\WindowsEventLogs.psd1|DSCResources\ConfigurationBase\ConfigurationBase.schema.psm1|DSCResources\LocalGroups\LocalGroups.schema.psm1|DSCResources\SoftwarePackages\SoftwarePackages.schema.psm1|DSCResources\WindowsEventLogs\WindowsEventLogs.schema.psm1|DSCResources\DscDiagnostic\DscDiagnostic.psd1|DSCResources\LocalUsers\LocalUsers.psd1|DSCResources\WebApplicationPools\WebApplicationPools.psd1|DSCResources\WindowsFeatures\WindowsFeatures.psd1|DSCResources\DscDiagnostic\DscDiagnostic.schema.psm1|DSCResources\LocalUsers\LocalUsers.schema.psm1|DSCResources\WebApplicationPools\WebApplicationPools.schema.psm1|DSCResources\WindowsFeatures\WindowsFeatures.schema.psm1|DSCResources\DscLcmController\DscLcmController.psd1|DSCResources\NetworkIpConfiguration\NetworkIpConfiguration.psd1|DSCResources\WebApplications\WebApplications.psd1|DSCResources\WindowsOptionalFeatures\WindowsOptionalFeatures.psd1|DSCResources\DscLcmController\DscLcmController.schema.psm1|DSCResources\NetworkIpConfiguration\NetworkIpConfiguration.schema.psm1|DSCResources\WebApplications\WebApplications.schema.psm1|DSCResources\WindowsOptionalFeatures\WindowsOptionalFeatures.schema.psm1|DSCResources\DscLcmMaintenanceWindows\DscLcmMaintenanceWindows.psd1|DSCResources\RegistryValues\RegistryValues.psd1|DSCResources\WebConfigProperties\WebConfigProperties.psd1|DSCResources\WindowsServices\WindowsServices.psd1|DSCResources\DscLcmMaintenanceWindows\DscLcmMaintenanceWindows.schema.psm1|DSCResources\RegistryValues\RegistryValues.schema.psm1|DSCResources\WebConfigProperties\WebConfigProperties.schema.psm1|DSCResources\WindowsServices\WindowsServices.schema.psm1|DSCResources\DscTagging\DscTagging.psd1|DSCResources\Scripts\Scripts.psd1|DSCResources\WebConfigPropertyCollections\WebConfigPropertyCollections.psd1|en-US\about_DscConfig.Demo.help.txt + 63e8bf79-62d3-4249-8fe6-9a766fbe8481 + 5.1 + DSC Community + + + D:\Git\DscWorkshop\output\RequiredModules\DscConfig.Demo\0.8.3 +
+
+
diff --git a/output/RequiredModules/DscConfig.Demo/0.8.3/en-US/about_DscConfig.Demo.help.txt b/output/RequiredModules/DscConfig.Demo/0.8.3/en-US/about_DscConfig.Demo.help.txt new file mode 100644 index 00000000..d98cc56e --- /dev/null +++ b/output/RequiredModules/DscConfig.Demo/0.8.3/en-US/about_DscConfig.Demo.help.txt @@ -0,0 +1,23 @@ +TOPIC + about_DscConfig.Demo + +SHORT DESCRIPTION + desc + +LONG DESCRIPTION + {{ Add Long description here }} + +EXAMPLES + PS C:\> {{ add examples here }} + +NOTE: + Thank you to all those who contributed to this module, by writing code, sharing opinions, and provided feedback. + +TROUBLESHOOTING NOTE: + Look out on the Github repository for issues and new releases. + +SEE ALSO + - {{ Please add Project URI such as github }}} + +KEYWORDS + {{ Add coma separated keywords here }} diff --git a/output/RequiredModules/FileSystemDsc/1.1.0/DSCResources/DSC_FileSystemAccessRule/DSC_FileSystemAccessRule.psm1 b/output/RequiredModules/FileSystemDsc/1.1.0/DSCResources/DSC_FileSystemAccessRule/DSC_FileSystemAccessRule.psm1 new file mode 100644 index 00000000..b8e29009 --- /dev/null +++ b/output/RequiredModules/FileSystemDsc/1.1.0/DSCResources/DSC_FileSystemAccessRule/DSC_FileSystemAccessRule.psm1 @@ -0,0 +1,612 @@ +$script:resourceHelperModulePath = Join-Path -Path $PSScriptRoot -ChildPath '..\..\Modules\DscResource.Common' + +Import-Module -Name $script:resourceHelperModulePath + +$script:localizedData = Get-LocalizedData -DefaultUICulture 'en-US' + +<# + .SYNOPSIS + Gets the rights of the specified filesystem object for the specified identity. + + .PARAMETER Path + The path to the item that should have permissions set. + + .PARAMETER Identity + The identity to set permissions for. +#> +function Get-TargetResource +{ + [CmdletBinding()] + [OutputType([System.Collections.Hashtable])] + param + ( + [Parameter(Mandatory = $true)] + [System.String] + $Path, + + [Parameter(Mandatory = $true)] + [System.String] + $Identity + ) + + Write-Verbose -Message ( + $script:localizedData.GetCurrentState -f $Identity, $Path + ) + + $result = @{ + Ensure = 'Absent' + Path = $Path + Identity = $Identity + Rights = [System.String[]] @() + IsActiveNode = $true + } + + if (-not (Test-Path -Path $Path)) + { + Write-Verbose -Message $script:localizedData.EvaluatingIfCluster + + $failoverClusterInstance = Get-CimInstance -Namespace 'root/MSCluster' -ClassName 'MSCluster_Cluster' -ErrorAction 'SilentlyContinue' + + if ($failoverClusterInstance) + { + Write-Verbose -Message ( + $script:localizedData.NodeIsClusterMember -f $env:COMPUTERNAME, $failoverClusterInstance.Name + ) + + $clusterPartition = Get-CimInstance -Namespace 'root/MSCluster' -ClassName 'MSCluster_ClusterDiskPartition' | + Where-Object -FilterScript { + $currentPartition = $_ + + # The property MountPoints is an array of mount points, e.g. @('D:', 'E:'). + $currentPartition.MountPoints | ForEach-Object -Process { + [regex]::Escape($Path) -match ('^{0}' -f $_) + } + } + + if ($clusterPartition) + { + Write-Verbose -Message ( + $script:localizedData.EvaluatingOwnerOfClusterDiskPartition -f @( + (Split-Path -Path $Path -Qualifier) + $env:COMPUTERNAME + ) + ) + + # Get the possible owner nodes for the partition. + [System.Array] $possibleOwners = $clusterPartition | + Get-CimAssociatedInstance -ResultClassName 'MSCluster_Resource' | + Get-CimAssociatedInstance -Association 'MSCluster_ResourceToPossibleOwner' | + Select-Object -ExpandProperty Name -Unique + + # Ensure the current node is a possible owner of the drive. + if ($possibleOwners -and $possibleOwners -contains $env:COMPUTERNAME) + { + Write-Verbose -Message ( + $script:localizedData.PossibleClusterResourceOwner -f @( + $env:COMPUTERNAME + $Path + ) + ) + + $result.IsActiveNode = $false + $result.Ensure = 'Present' + } + else + { + Write-Verbose -Message ( + $script:localizedData.NotPossibleClusterResourceOwner -f @( + $env:COMPUTERNAME + $Path + ) + ) + } + } + else + { + Write-Verbose -Message ( + $script:localizedData.NoClusterDiskPartitionFound -f $Path + ) + } + } + else + { + Write-Verbose -Message $script:localizedData.NodeIsNotClusterMember + } + + <# + Evaluates if the path was not found and the node is the active node. + The node is always assumed to be the active node unless the node is + a possible member of a cluster disk partition the path belong to. + If the node could be a possible member but currently was not the active + node then the property IsActiveNode was set to $false. + #> + if ($result.IsActiveNode) + { + Write-Warning -Message ( + $script:localizedData.PathDoesNotExist -f $Path + ) + } + } + else + { + Write-Verbose -Message ( + $script:localizedData.PathExist -f $Identity + ) + + $acl = Get-ACLAccess -Path $Path + $accessRules = $acl.Access + + <# + Set-TargetResource works without BUILTIN\, but Get-TargetResource fails + (silently) without this logic. This is regression tested by the 'Users' + group in the test logic, which is actually BUILTIN\USERS per ACLs, + however this is not obvious to users and results in unexpected + functionality such as successfully running Set-TargetResource, but + result in Test-TargetResource that fail every time. This regex + workaround for the common windows identifier prefixes makes behavior + consistent. Local groups are fully qualified with "$env:COMPUTERNAME\". + #> + $regexEscapedIdentity = [System.Text.RegularExpressions.Regex]::Escape($Identity) + $escapedComputerName = [System.Text.RegularExpressions.Regex]::Escape($env:COMPUTERNAME) + + $regex = "^(NT AUTHORITY|BUILTIN|NT SERVICES|$escapedComputerName)\\$regexEscapedIdentity" + + $matchingRules = $accessRules | Where-Object -FilterScript { + $_.IdentityReference -eq $Identity ` + -or $_.IdentityReference -match $regex + } + + if ($matchingRules) + { + $result.Ensure = 'Present' + $result.Rights = @( + ($matchingRules.FileSystemRights -split ', ') | Select-Object -Unique + ) + } + } + + return $result +} + +<# + .SYNOPSIS + Sets the rights of the specified filesystem object for the specified + identity. + + .PARAMETER Path + The path to the item that should have permissions set. + + .PARAMETER Identity + The identity to set permissions for. + + .PARAMETER Rights + The permissions to include in this rule. Optional if Ensure is set to + value 'Absent'. + + .PARAMETER Ensure + Present to create the rule, Absent to remove an existing rule. Default + value is 'Present'. + + .PARAMETER ProcessOnlyOnActiveNode + Specifies that the resource will only determine if a change is needed if + the target node is the active host of the filesystem object. The user the + configuration is run as must have permission to the Windows Server Failover + Cluster. + + Not used in Set-TargetResource. + + .NOTES + This function uses Set-Acl that was first introduced in + Windows Powershell 5.1. +#> +function Set-TargetResource +{ + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [System.String] + $Path, + + [Parameter(Mandatory = $true)] + [System.String] + $Identity, + + [Parameter()] + [ValidateSet( + 'ListDirectory', + 'ReadData', + 'WriteData', + 'CreateFiles', + 'CreateDirectories', + 'AppendData', + 'ReadExtendedAttributes', + 'WriteExtendedAttributes', + 'Traverse', + 'ExecuteFile', + 'DeleteSubdirectoriesAndFiles', + 'ReadAttributes', + 'WriteAttributes', + 'Write', + 'Delete', + 'ReadPermissions', + 'Read', + 'ReadAndExecute', + 'Modify', + 'ChangePermissions', + 'TakeOwnership', + 'Synchronize', + 'FullControl' + )] + [System.String[]] + $Rights, + + [Parameter()] + [ValidateSet('Present', 'Absent')] + [System.String] + $Ensure = 'Present', + + [Parameter()] + [System.Boolean] + $ProcessOnlyOnActiveNode + ) + + if (-not (Test-Path -Path $Path)) + { + $errorMessage = $script:localizedData.PathDoesNotExist -f $Path + + New-ObjectNotFoundException -Message $errorMessage + } + + $acl = Get-ACLAccess -Path $Path + + if ($Ensure -eq 'Present') + { + # Validate the rights parameter was passed. + if (-not $PSBoundParameters.ContainsKey('Rights')) + { + $errorMessage = $script:localizedData.NoRightsWereSpecified -f $Identity, $Path + + New-InvalidArgumentException -ArgumentName 'Rights' -Message $errorMessage + } + + Write-Verbose -Message ( + $script:localizedData.SetAllowAccessRule -f ($Rights -join ', '), $Identity, $Path + ) + + $newFileSystemAccessRuleParameters = @{ + TypeName = 'System.Security.AccessControl.FileSystemAccessRule' + ArgumentList = @( + $Identity, + [System.Security.AccessControl.FileSystemRights] $Rights, + 'ContainerInherit,ObjectInherit', + 'None', + 'Allow' + ) + } + + $fileSystemAccessRule = New-Object @newFileSystemAccessRuleParameters + + $acl.SetAccessRule($fileSystemAccessRule) + } + + if ($Ensure -eq 'Absent') + { + # If no rights were passed. + if (-not $PSBoundParameters.ContainsKey('Rights')) + { + # Set rights to an empty array. + $Rights = @() + } + + <# + If no specific rights was provided then purge all rights for the + identity, otherwise remove just the specific rights. + #> + if ($Rights.Count -eq 0) + { + $identityRules = $acl.Access | + Where-Object -FilterScript { + $_.IdentityReference -eq $Identity + } + + $identityRule = $identityRules | + Select-Object -First 1 + + if ($identityRule) + { + Write-Verbose -Message ( + $script:localizedData.RemoveAllAllowAccessRules -f $Identity, $Path + ) + + $acl.PurgeAccessRules($identityRule.IdentityReference) + } + } + else + { + foreach ($right in $Rights) + { + Write-Verbose -Message ( + $script:localizedData.RemoveAllowAccessRule -f $right, $Identity, $Path + ) + + $removeFileSystemAccessRuleParameters = @{ + TypeName = 'System.Security.AccessControl.FileSystemAccessRule' + ArgumentList = @( + $Identity, + [System.Security.AccessControl.FileSystemRights] $right, + 'ContainerInherit,ObjectInherit', + 'None', + 'Allow' + ) + } + + $fileSystemAccessRule = New-Object @removeFileSystemAccessRuleParameters + + $null = $acl.RemoveAccessRule($fileSystemAccessRule) + } + } + } + + try + { + Set-Acl -Path $Path -AclObject $acl + } + catch + { + $errorMessage = $script:localizedData.FailedToSetAccessRules -f $Path + + New-InvalidOperationException -Message $errorMessage -ErrorRecord $_ + } +} + +<# + .SYNOPSIS + Tests the rights of the specified filesystem object for the specified + identity. + + .PARAMETER Path + The path to the item that should have permissions set. + + .PARAMETER Identity + The identity to set permissions for. + + .PARAMETER Rights + The permissions to include in this rule. Optional if Ensure is set to + value 'Absent'. + + .PARAMETER Ensure + Present to create the rule, Absent to remove an existing rule. Default + value is 'Present'. + + .PARAMETER ProcessOnlyOnActiveNode + Specifies that the resource will only determine if a change is needed if + the target node is the active host of the filesystem object. The user the + configuration is run as must have permission to the Windows Server Failover + Cluster. +#> +function Test-TargetResource +{ + [CmdletBinding()] + [OutputType([System.Boolean])] + param + ( + [Parameter(Mandatory = $true)] + [System.String] + $Path, + + [Parameter(Mandatory = $true)] + [System.String] + $Identity, + + [Parameter()] + [ValidateSet( + 'ListDirectory', + 'ReadData', + 'WriteData', + 'CreateFiles', + 'CreateDirectories', + 'AppendData', + 'ReadExtendedAttributes', + 'WriteExtendedAttributes', + 'Traverse', + 'ExecuteFile', + 'DeleteSubdirectoriesAndFiles', + 'ReadAttributes', + 'WriteAttributes', + 'Write', + 'Delete', + 'ReadPermissions', + 'Read', + 'ReadAndExecute', + 'Modify', + 'ChangePermissions', + 'TakeOwnership', + 'Synchronize', + 'FullControl' + )] + [System.String[]] + $Rights, + + [Parameter()] + [ValidateSet('Present', 'Absent')] + [System.String] + $Ensure = 'Present', + + [Parameter()] + [System.Boolean] + $ProcessOnlyOnActiveNode + ) + + $result = $true + + $getTargetResourceParameters = @{ + Path = $Path + Identity = $Identity + } + + $currentState = Get-TargetResource @getTargetResourceParameters + + <# + If this is supposed to process on the active node, and this is not the + active node, don't bother evaluating the test. + #> + if ($ProcessOnlyOnActiveNode -and -not $currentState.IsActiveNode) + { + Write-Verbose -Message ( + $script:localizedData.IsNotActiveNode -f $env:COMPUTERNAME, $Path + ) + + return $result + } + + Write-Verbose -Message ( + $script:localizedData.EvaluatingRights -f $Identity + ) + + switch ($Ensure) + { + 'Absent' + { + # If no rights were passed. + if (-not $PSBoundParameters.ContainsKey('Rights')) + { + # Set rights to an empty array. + $Rights = @() + } + + if ($currentState.Rights -and (-not $Rights)) + { + $result = $false + + Write-Verbose -Message ( + $script:localizedData.AbsentRightsNotInDesiredState -f $Identity, ($currentState.Rights -join ', ') + ) + } + elseif (-not $currentState.Rights) + { + $result = $true + + Write-Verbose -Message ( + $script:localizedData.InDesiredState -f $Identity + ) + } + # Always hit, but just clarifying what the actual case is by filling in the if block. + elseif ($Rights) + { + Write-Verbose -Message ( + $script:localizedData.EvaluatingIndividualRight -f $Identity, ($currentState.Rights -join ', '), ($Rights -join ', ') + ) + + foreach ($right in $Rights) + { + $rightNotAllowed = [System.Security.AccessControl.FileSystemRights] $right + $currentRights = [System.Security.AccessControl.FileSystemRights] $currentState.Rights + + # If any rights that we want to deny are individually a full subset of existing rights. + $currentRightResult = -not ($rightNotAllowed -eq ($rightNotAllowed -band $currentRights)) + + if (-not $currentRightResult) + { + Write-Verbose -Message ( + $script:localizedData.IndividualRightNotInDesiredState -f $Identity, $rightNotAllowed + ) + } + else + { + Write-Verbose -Message ( + $script:localizedData.IndividualRightInDesiredState -f $rightNotAllowed + ) + } + + $result = $result -and $currentRightResult + } + } + } + + 'Present' + { + # Validate the rights parameter was passed. + if (-not $PSBoundParameters.ContainsKey('Rights')) + { + $errorMessage = $script:localizedData.NoRightsWereSpecified -f $Identity, $Path + + New-InvalidArgumentException -ArgumentName 'Rights' -Message $errorMessage + } + + <# + This isn't always the same as the input if parts of the input are subset + permissions, so pre-cast it. For example: + + [System.Security.AccessControl.FileSystemRights] @('Modify', 'Read', 'Write') + + is actually just 'Modify' within the flagged enum, so test as such to + avoid false test failures. + #> + $expected = [System.Security.AccessControl.FileSystemRights] $Rights + + $result = $false + + if ($currentState.Rights) + { + <# + At minimum the AND result of the current and expected rights + should be the expected rights (allow extra rights, but not + missing). Otherwise permission flags are missing from the enum. + #> + $result = $expected -eq ($expected -band ([System.Security.AccessControl.FileSystemRights] $currentState.Rights)) + } + + if ($result) + { + Write-Verbose -Message ( + $script:localizedData.InDesiredState -f $Identity + ) + } + else + { + Write-Verbose -Message ( + $script:localizedData.NotInDesiredState -f @( + $Identity, + ($currentState.Rights -join ', '), + $expected, + ($Rights -join ', ' ) + ) + ) + } + } + } + + return $result +} + +<# + .SYNOPSIS + This function is wrapper for getting the DACL for the specified path. + + .PARAMETER Path + The path to the item that should have permissions set. + + .NOTES + "Well the limited features of Get-ACL means that you always read the full + security descriptor including the owner whether you intended to or not. + That means that when you come to write to the object based on a modified + version of what you read, you are attempting to write back to the owner + attribute. + + The GetAccessControl('Access') method reads only the DACL so when you + write it back you are not trying to write something you did not intend to." + https://www.mickputley.net/2015/11/set-acl-security-identifier-is-not.html + https://github.com/dsccommunity/FileSystemDsc/issues/3 +#> +function Get-ACLAccess +{ + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [System.String] + $Path + ) + return (Get-Item -Path $Path).GetAccessControl('Access') +} diff --git a/output/RequiredModules/FileSystemDsc/1.1.0/DSCResources/DSC_FileSystemAccessRule/DSC_FileSystemAccessRule.schema.mof b/output/RequiredModules/FileSystemDsc/1.1.0/DSCResources/DSC_FileSystemAccessRule/DSC_FileSystemAccessRule.schema.mof new file mode 100644 index 00000000..3f21cdf2 --- /dev/null +++ b/output/RequiredModules/FileSystemDsc/1.1.0/DSCResources/DSC_FileSystemAccessRule/DSC_FileSystemAccessRule.schema.mof @@ -0,0 +1,10 @@ +[ClassVersion("1.0.0.0"), FriendlyName("FileSystemAccessRule")] +class DSC_FileSystemAccessRule : OMI_BaseResource +{ + [Key, Description("The path to the item that should have permissions set.")] string Path; + [Key, Description("The identity to set permissions for.")] string Identity; + [Write, Description("The permissions to include in this rule. Optional if Ensure is set to value 'Absent'."), ValueMap{"ListDirectory","ReadData","WriteData","CreateFiles","CreateDirectories","AppendData","ReadExtendedAttributes","WriteExtendedAttributes","Traverse","ExecuteFile","DeleteSubdirectoriesAndFiles","ReadAttributes","WriteAttributes","Write","Delete","ReadPermissions","Read","ReadAndExecute","Modify","ChangePermissions","TakeOwnership","Synchronize","FullControl"}, Values{"ListDirectory","ReadData","WriteData","CreateFiles","CreateDirectories","AppendData","ReadExtendedAttributes","WriteExtendedAttributes","Traverse","ExecuteFile","DeleteSubdirectoriesAndFiles","ReadAttributes","WriteAttributes","Write","Delete","ReadPermissions","Read","ReadAndExecute","Modify","ChangePermissions","TakeOwnership","Synchronize","FullControl"}] string Rights[]; + [Write, Description("Present to create the rule, Absent to remove an existing rule. Default value is 'Present'."), ValueMap{"Present","Absent"}, Values{"Present","Absent"}] string Ensure; + [Write, Description("Specifies that the resource will only determine if a change is needed if the target node is the active host of the filesystem object. The user the configuration is run as must have permission to the Windows Server Failover Cluster.")] Boolean ProcessOnlyOnActiveNode; + [Read, Description("Determines if the current node is actively hosting the filesystem object.")] Boolean IsActiveNode; +}; diff --git a/output/RequiredModules/FileSystemDsc/1.1.0/DSCResources/DSC_FileSystemAccessRule/README.md b/output/RequiredModules/FileSystemDsc/1.1.0/DSCResources/DSC_FileSystemAccessRule/README.md new file mode 100644 index 00000000..11483dd2 --- /dev/null +++ b/output/RequiredModules/FileSystemDsc/1.1.0/DSCResources/DSC_FileSystemAccessRule/README.md @@ -0,0 +1,22 @@ +# Description + +The DSC resource `FileSystemAccessRule` will manage permissions on paths +in the file system. + +If the parameter `Rights` is not used or no rights are provided (using +`Rights = @()`), and `Ensure` is set to `'Absent'` then all the allow +access rules for the identity will be removed. If specific rights are +specified then only those rights will be removed when `Ensure` is set to +`'Absent'`. + +When calling the method `Get` the property `Ensure` will be set to `'Present'` +if the identity is found with an access rule on the specified path. + +If the resource is used for a path that belongs to a Windows Server Failover +Cluster's cluster disk partition and if the node it runs on is not the +active node, then the method `Get` will return the value `Present` for +the property `Ensure`, but the property `Rights` will be empty. + +## Requirements + +- Currently only works with the Windows file system. diff --git a/output/RequiredModules/FileSystemDsc/1.1.0/DSCResources/DSC_FileSystemAccessRule/en-US/DSC_FileSystemAccessRule.strings.psd1 b/output/RequiredModules/FileSystemDsc/1.1.0/DSCResources/DSC_FileSystemAccessRule/en-US/DSC_FileSystemAccessRule.strings.psd1 new file mode 100644 index 00000000..3518773f --- /dev/null +++ b/output/RequiredModules/FileSystemDsc/1.1.0/DSCResources/DSC_FileSystemAccessRule/en-US/DSC_FileSystemAccessRule.strings.psd1 @@ -0,0 +1,27 @@ +# Localized resources for Folder + +ConvertFrom-StringData @' + GetCurrentState = Getting the current state of the access rules for the identity '{0}' for the path '{1}'. (FSAR0001) + PathDoesNotExist = Unable to evaluate the access rules for the path '{0}' because the path does not exist. (FSAR0002) + EvaluatingIfCluster = Could not find the path on the node. Evaluating if the node belongs to a Windows Server Failover Cluster and if the path belongs to a cluster disk partition. (FSAR0003) + NodeIsClusterMember = The node '{0}' is a member of the Windows Server Failover Cluster '{1}'. Evaluating cluster disk partitions. (FSAR0004) + EvaluatingOwnerOfClusterDiskPartition = Found a cluster disk partition with the mount point '{0}'. Evaluating if the node '{1}' is a possible owner. (FSAR0005) + PossibleClusterResourceOwner = The node '{0}' is a possible owner for the path '{1}' but it is currently not the active node. (FSAR0006) + NotPossibleClusterResourceOwner = The node '{0}' is not a possible owner for the path '{1}'. (FSAR0007) + NoClusterDiskPartitionFound = No cluster disk partition was found that the path '{0}' could belong to. (FSAR0008) + NodeIsNotClusterMember = Node does not belong to a Windows Server Failover Cluster. (FSAR0009) + PathExist = Found the path on the node. Evaluating access rules for identity '{0}'. (FSAR0010) + IsNotActiveNode = The node '{0}' is not actively hosting the path '{1}'. Exiting the test. (FSAR0011) + AbsentRightsNotInDesiredState = The identity '{0}' has the rights '{1}', but expected the identity to have no rights. (FSAR0012) + InDesiredState = The rights for the identity '{0}' are in desired state. (FSAR0013) + EvaluatingIndividualRight = The identity '{0}' currently have the rights '{1}', and should not have '{2}'. Evaluating each individual right against the current state. (FSAR0014) + IndividualRightNotInDesiredState = The right '{1}' is not in desired state. The identity '{0}' should not have the right '{1}', but as part of the rights in the current state the identity is given that right. (FSAR0015) + IndividualRightInDesiredState = The right '{0}' is in desired state. The right is not included with any of the rights in the current state. (FSAR0016) + NoRightsWereSpecified = No rights were specified for the identity '{0}' for the path '{1}'. (FSAR0017) + EvaluatingRights = Evaluating the rights for the identity '{0}'. (FSAR0018) + NotInDesiredState = The identity '{0}' has the rights '{1}', but expected the rights to also include '{2}' (combined from desired rights '{3}'). (FSAR0019) + FailedToSetAccessRules = Failed to set the changed access rules for the path '{0}'. (FSAR0020) + SetAllowAccessRule = Setting the allow access rules '{0}' for the identity '{1}' on the path '{2}'. (FSAR0021) + RemoveAllAllowAccessRules = Removing all allow access rules for the identity '{0}' on the path '{1}'. (FSAR0022) + RemoveAllowAccessRule = Removing the allow access rule '{0}' for the identity '{1}' on the path '{2}'. (FSAR0023) +'@ diff --git a/output/RequiredModules/FileSystemDsc/1.1.0/DSCResources/DSC_FileSystemAccessRule/en-US/about_FileSystemAccessRule.help.txt b/output/RequiredModules/FileSystemDsc/1.1.0/DSCResources/DSC_FileSystemAccessRule/en-US/about_FileSystemAccessRule.help.txt new file mode 100644 index 00000000..4edd54c8 --- /dev/null +++ b/output/RequiredModules/FileSystemDsc/1.1.0/DSCResources/DSC_FileSystemAccessRule/en-US/about_FileSystemAccessRule.help.txt @@ -0,0 +1,80 @@ +.NAME + FileSystemAccessRule + +.DESCRIPTION + The DSC resource `FileSystemAccessRule` will manage permissions on paths + in the file system. + + If the parameter `Rights` is not used or no rights are provided (using + `Rights = @()`), and `Ensure` is set to `'Absent'` then all the allow + access rules for the identity will be removed. If specific rights are + specified then only those rights will be removed when `Ensure` is set to + `'Absent'`. + + When calling the method `Get` the property `Ensure` will be set to `'Present'` + if the identity is found with an access rule on the specified path. + + If the resource is used for a path that belongs to a Windows Server Failover + Cluster's cluster disk partition and if the node it runs on is not the + active node, then the method `Get` will return the value `Present` for + the property `Ensure`, but the property `Rights` will be empty. + + ## Requirements + + - Currently only works with the Windows file system. + +.PARAMETER Path + Key - String + The path to the item that should have permissions set. + +.PARAMETER Identity + Key - String + The identity to set permissions for. + +.PARAMETER Rights + Write - StringArray + Allowed values: ListDirectory, ReadData, WriteData, CreateFiles, CreateDirectories, AppendData, ReadExtendedAttributes, WriteExtendedAttributes, Traverse, ExecuteFile, DeleteSubdirectoriesAndFiles, ReadAttributes, WriteAttributes, Write, Delete, ReadPermissions, Read, ReadAndExecute, Modify, ChangePermissions, TakeOwnership, Synchronize, FullControl + The permissions to include in this rule. Optional if Ensure is set to value 'Absent'. + +.PARAMETER Ensure + Write - String + Allowed values: Present, Absent + Present to create the rule, Absent to remove an existing rule. Default value is 'Present'. + +.PARAMETER ProcessOnlyOnActiveNode + Write - Boolean + Specifies that the resource will only determine if a change is needed if the target node is the active host of the filesystem object. The user the configuration is run as must have permission to the Windows Server Failover Cluster. + +.PARAMETER IsActiveNode + Read - Boolean + Determines if the current node is actively hosting the filesystem object. + +.EXAMPLE 1 + +This configuration will add the full control right to the folder +'C:\some\path' for the identity NT AUTHORITY\NETWORK SERVICE, and +add the read right to the folder 'C:\other\path' for the local group +Users. + +Configuration FileSystemAccessRule_SetPermissionOnFolder_Config +{ + Import-DscResource -ModuleName FileSystemDsc + + node localhost + { + FileSystemAccessRule 'AddRightFullControl' + { + Path = 'C:\some\path' + Identity = 'NT AUTHORITY\NETWORK SERVICE' + Rights = @('FullControl') + } + + FileSystemAccessRule 'AddRightRead' + { + Path = 'C:\other\path' + Identity = 'Users' + Rights = @('Read') + } + } +} + diff --git a/output/RequiredModules/FileSystemDsc/1.1.0/FileSystemDsc.psd1 b/output/RequiredModules/FileSystemDsc/1.1.0/FileSystemDsc.psd1 new file mode 100644 index 00000000..cce9962c --- /dev/null +++ b/output/RequiredModules/FileSystemDsc/1.1.0/FileSystemDsc.psd1 @@ -0,0 +1,100 @@ +@{ + RootModule = 'FileSystemDsc.psm1' + # Version number of this module. + ModuleVersion = '1.1.0' + + # ID used to uniquely identify this module + GUID = '86a20a80-3bcd-477e-9b90-ec8d52fbe415' + + CompatiblePSEditions = @('Core', 'Desktop') + + # Author of this module + Author = 'DSC Community' + + # Company or vendor of this module + CompanyName = 'DSC Community' + + # Copyright statement for this module + Copyright = 'Copyright the DSC Community contributors. All rights reserved.' + + # Description of the functionality provided by this module + Description = 'This module contains DSC resources for managing file systems.' + + # Minimum version of the Windows PowerShell engine required by this module + PowerShellVersion = '5.1' + + # Minimum version of the common language runtime (CLR) required by this module + CLRVersion = '4.0' + + DscResourcesToExport = @('FileSystemObject','FileSystemAccessRule') + + RequiredAssemblies = @() + + # Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell. + PrivateData = @{ + PSData = @{ + # Set to a prerelease string value if the release should be a prerelease. + Prerelease = 'filesystemobject0001' + + # Tags applied to this module. These help with module discovery in online galleries. + Tags = @('DesiredStateConfiguration', 'DSC', 'DSCResourceKit', 'DSCResource') + + # A URL to the license for this module. + LicenseUri = 'https://github.com/dsccommunity/FileSystemDsc/blob/main/LICENSE' + + # A URL to the main website for this project. + ProjectUri = 'https://github.com/dsccommunity/FileSystemDsc' + + # A URL to an icon representing this module. + IconUri = 'https://dsccommunity.org/images/DSC_Logo_300p.png' + + # ReleaseNotes of this module + ReleaseNotes = '## [1.1.0-filesystemobject0001] - 2023-06-11 + +### Added + +- FileSystemDsc + - Added issue and pull request templates to help contributors. + - Added wiki generation and publish to GitHub repository wiki. + - Added recommended VS Code extensions. + - Added settings for VS Code extension _Pester Test Adapter_. + +### Changed + +- FileSystemDsc + - Renamed `master` branch to `main` ([issue #11](https://github.com/dsccommunity/FileSystemDsc/issues/11)). + - Only run the CI pipeline on branch `master` when there are changes to + files inside the `source` folder. + - The regular expression for `minor-version-bump-message` in the file + `GitVersion.yml` was changed to only raise minor version when the + commit message contain the word `add`, `adds`, `minor`, `feature`, + or `features`. + - Added missing MIT LICENSE file. + - Converted tests to Pester 5. + - Minor changes to pipeline files. + - Update build configuration to use Pester advanced build configuration. + - Update pipeline to user Sampler GitHub tasks. + - Update pipeline deploy step to correctly download build artifact. + - Update so that HQRM test correctly creates a NUnit file that can be + uploaded to Azure Pipelines. + - Updated pipeline to use the new faster Pester Code coverage. + - Using the latest Pester preview version in the pipeline to be able to + test new Pester functionality. + - Updated build pipeline files. + +### Fixed + +- FileSystemDsc + - The component `gitversion` that is used in the pipeline was wrongly configured + when the repository moved to the new default branch `main`. It no longer throws + an error when using newer versions of GitVersion. + - Fix pipeline to use available build workers. +- FileSystemAccessRule + - Unit test was updated to support latest Pester. + - Test was updated to handle that `build.ps1` has not been run. + +' + + } # End of PSData hashtable + } # End of PrivateData hashtable +} diff --git a/output/RequiredModules/FileSystemDsc/1.1.0/FileSystemDsc.psm1 b/output/RequiredModules/FileSystemDsc/1.1.0/FileSystemDsc.psm1 new file mode 100644 index 00000000..88de9836 --- /dev/null +++ b/output/RequiredModules/FileSystemDsc/1.1.0/FileSystemDsc.psm1 @@ -0,0 +1,451 @@ +#Region '.\Enum\checksumType.ps1' 0 +enum checksumType +{ + md5 + LastModifiedTime + CreationTime +} +#EndRegion '.\Enum\checksumType.ps1' 7 +#Region '.\Enum\encoding.ps1' 0 +enum encoding +{ + ASCII + Latin1 + UTF7 + UTF8 + UTF32 + BigEndianUnicode + Default + Unicode +} +#EndRegion '.\Enum\encoding.ps1' 12 +#Region '.\Enum\ensure.ps1' 0 +enum ensure +{ + present + absent +} + +#EndRegion '.\Enum\ensure.ps1' 7 +#Region '.\Enum\linkBehavior.ps1' 0 +enum linkBehavior +{ + follow + manage +} +#EndRegion '.\Enum\linkBehavior.ps1' 6 +#Region '.\Enum\objectType.ps1' 0 +enum objectType +{ + file + directory + symboliclink +} +#EndRegion '.\Enum\objectType.ps1' 7 +#Region '.\Classes\002.FileSystemObject.ps1' 0 +class FileSystemDscReason +{ + [DscProperty()] + [System.String] + $Code + + [DscProperty()] + [System.String] + $Phrase +} + +<# + .SYNOPSIS + The File resource enables file system operations on Linux and Windows. + With regards to parameters and globbing, it behaves like the Item and Content + cmdlets. + + .PARAMETER DestinationPath + The path to create/copy to. + + .PARAMETER SourcePath + If data should be copied, the source path to copy from. + .PARAMETER Ensure + Indicates if destination should be created or removed. Values: Absent, Present. Default: Present. + + .PARAMETER Type + The type of the object to create. Values: file, directory, symboliclink. Default: directory + + .PARAMETER Contents + The file contents. Unused if type is directory + + .PARAMETER Checksum + The type of checksum to use for copy operations. Values: md5, CreationTime, LastModifiedTime. Default: md5 + + .PARAMETER Recurse + Indicates that recurse should be used if data is copied. + + .PARAMETER Force + Indicates that folder structures should be created and existing files overwritten + + .PARAMETER Links + Link behavior, currently not implemented. Values: follow, manage. Default: follow + + .PARAMETER Group + Linux group name for chown, currently not implemented. + + .PARAMETER Mode + Linux mode for chmod, currently not implemented. + + .PARAMETER Owner + Linux owner name for chown, currently not implemented. + + .PARAMETER Encoding + File encoding, used with Contents. Values: ASCII, Latin1, UTF7, UTF8, UTF32, BigEndianUnicode, Default, Unicode. Default: Default + + .PARAMETER IgnoreTrailingWhitespace + Indicates that trailing whitespace should be ignored when comparing file contents. +#> +[DscResource()] +class FileSystemObject +{ + [DscProperty(Key)] + [string] + $DestinationPath + + [DscProperty()] + [string] + $SourcePath + + [DscProperty()] + [ensure] + $Ensure = [ensure]::present + + [DscProperty()] + [objectType] + $Type = [objectType]::directory + + [DscProperty()] + [string] + $Contents + + [DscProperty()] + [checksumType] + $Checksum = [checksumType]::md5 + + [DscProperty()] + [bool] + $Recurse = $false + + [DscProperty()] + [bool] + $Force = $false + + [DscProperty()] + [linkBehavior] + $Links = [linkBehavior]::follow + + [DscProperty()] + [string] + $Group + + [DscProperty()] + [string] + $Mode + + [DscProperty()] + [string] + $Owner + + [DscProperty(NotConfigurable)] + [datetime] + $CreatedDate + + [DscProperty(NotConfigurable)] + [datetime] + $ModifiedDate + + [DscProperty()] + [encoding] + $Encoding = 'Default' + + [DscProperty()] + [bool] + $IgnoreTrailingWhitespace + + [DscProperty(NotConfigurable)] + [FileSystemDscReason[]] + $Reasons + + [FileSystemObject] Get () + { + $returnable = @{ + DestinationPath = $this.DestinationPath + SourcePath = $this.SourcePath + Ensure = $this.Ensure + Type = $this.Type + Contents = '' + Checksum = $this.Checksum + Recurse = $this.Recurse + Force = $this.Force + Links = $this.Links + Encoding = $this.Encoding + Group = '' + Mode = '' + Owner = '' + IgnoreTrailingWhitespace = $this.IgnoreTrailingWhitespace + CreatedDate = [datetime]::new(0) + ModifiedDate = [datetime]::new(0) + Reasons = @() + } + + if ($this.Type -eq [objectType]::directory -and -not [string]::IsNullOrWhiteSpace($this.Contents)) + { + Write-Verbose -Message "Type is directory, yet parameter Contents was used." + $returnable.Reasons += @{ + Code = "File:File:ParameterMismatch" + Phrase = "Type is directory, yet parameter Contents was used." + } + return [FileSystemObject]$returnable + } + + $object = Get-Item -ErrorAction SilentlyContinue -Path $this.DestinationPath -Force + if ($null -eq $object -and $this.Ensure -eq [ensure]::present) + { + Write-Verbose -Message "Object $($this.DestinationPath) does not exist, but Ensure is set to 'Present'" + $returnable.Reasons += @{ + Code = "File:File:ObjectMissingWhenItShouldExist" + Phrase = "Object $($this.DestinationPath) does not exist, but Ensure is set to 'Present'" + } + return [FileSystemObject]$returnable + } + + if ($null -ne $object -and $this.Ensure -eq [ensure]::absent) + { + Write-Verbose -Message "Object $($this.DestinationPath) exists, but Ensure is set to 'Absent'" + $returnable.Reasons += @{ + Code = "File:File:ObjectExistsWhenItShouldNot" + Phrase = "Object $($this.DestinationPath) exists, but Ensure is set to 'Absent'" + } + return [FileSystemObject]$returnable + } + + if ($object.Count -eq 1 -and ($object.Attributes -band 'ReparsePoint') -eq 'ReparsePoint') + { + $returnable.Type = 'SymbolicLink' + } + elseif ($object.Count -eq 1 -and ($object.Attributes -band 'Directory') -eq 'Directory') + { + $returnable.Type = 'Directory' + } + elseif ($object.Count -eq 1) + { + $returnable.Type = 'File' + } + + if ($returnable.Type -ne $this.Type) + { + $returnable.Reasons += @{ + Code = "File:File:TypeMismatch" + Phrase = "Type of $($object.FullName) has type '$($returnable.Type)', should be '$($this.Type)'" + } + } + + $returnable.DestinationPath = $object.FullName + if ([string]::IsNullOrWhiteSpace($this.SourcePath) -and $object -and $this.Type -eq [objectType]::file) + { + $returnable.Contents = Get-Content -Raw -Path $object.FullName -Encoding $this.Encoding.ToString() + } + + if (-not $this.Ensure -eq 'Absent' -and -not [string]::IsNullOrWhiteSpace($returnable.Contents) -and $this.IgnoreTrailingWhitespace) + { + $returnable.Contents = $returnable.Contents.Trim() + } + + if (-not [string]::IsNullOrWhiteSpace($this.Contents) -and $returnable.Contents -ne $this.Contents) + { + $returnable.Reasons += @{ + Code = "File:File:ContentMismatch" + Phrase = "Content of $($object.FullName) different from parameter Contents" + } + } + + if ($object.Count -eq 1) + { + $returnable.CreatedDate = $object.CreationTime + $returnable.ModifiedDate = $object.LastWriteTime + $returnable.Owner = $object.User + $returnable.Mode = $object.Mode + $returnable.Group = $object.Group + } + + if (-not [string]::IsNullOrWhiteSpace($this.SourcePath)) + { + if (-not $this.Recurse -and $this.Type -eq [objectType]::directory) + { + Write-Verbose -Message "Directory is copied without Recurse parameter. Skipping file checksum" + return [FileSystemObject]$returnable + } + + #$destination = if (-not $this.Recurse -and $this.SourcePath -notmatch '\*\?\[\]') + #{ + # Join-Path $this.DestinationPath (Split-Path $this.SourcePath -Leaf) + #} + #else + #{ + # $this.DestinationPath + #} + $destination = $this.DestinationPath + + if (-not (Test-Path -Path $destination)) + { + Write-Verbose -Message "The destination path '$destination' does not exist" + $returnable.Reasons += @{ + Code = "File:File:DestinationPathNotFound" + Phrase = "The destination path '$destination' does not exist" + } + } + + if (-not (Test-Path -Path $this.SourcePath)) + { + Write-Verbose -Message "The source path '$($this.SourcePath)' does not exist" + $returnable.Reasons += @{ + Code = "File:File:SourcePathNotFound" + Phrase = "The source path '$($this.SourcePath)' does not exist" + } + } + + if ((Test-Path -Path $destination) -and (Test-Path -Path $this.SourcePath)) + { + $currHash = $this.CompareHash($destination, $this.SourcePath, $this.Checksum, $this.Recurse) + + if ($currHash.Count -gt 0) + { + Write-Verbose -Message "Hashes of files in $($this.DestinationPath) (comparison path used: $destination) different from hashes in $($this.SourcePath)" + $returnable.Reasons += @{ + Code = "File:File:HashMismatch" + Phrase = "Hashes of files in $($this.DestinationPath) different from hashes in $($this.SourcePath)" + } + } + } + } + return [FileSystemObject]$returnable + } + + [void] Set() + { + if ($this.Ensure -eq 'Absent') + { + Write-Verbose -Message "Removing $($this.DestinationPath) with Recurse and Force" + Remove-Item -Recurse -Force -Path $this.DestinationPath + return + } + + if ($this.Type -in [objectType]::file, [objectType]::directory -and [string]::IsNullOrWhiteSpace($this.SourcePath)) + { + Write-Verbose -Message "Creating new $($this.Type) $($this.DestinationPath), Force" + $param = @{ + ItemType = $this.Type + Path = $this.DestinationPath + } + if ($this.Force) + { + $param['Force'] = $true + } + $null = New-Item @param + } + + if ($this.Type -eq [objectType]::SymbolicLink) + { + Write-Verbose -Message "Creating new symbolic link $($this.DestinationPath) --> $($this.SourcePath)" + New-Item -ItemType SymbolicLink -Path $this.DestinationPath -Value $this.SourcePath + return + } + + if ($this.Contents) + { + Write-Verbose -Message "Setting content of $($this.DestinationPath) using $($this.Encoding)" + $this.Contents | Set-Content -Path $this.DestinationPath -Force -Encoding $this.Encoding.ToString() -NoNewline + } + + if ($this.SourcePath -and ($this.SourcePath -match '\*|\?\[\]') -and -not (Test-Path -Path $this.DestinationPath)) + { + Write-Verbose -Message "Creating destination directory for wildcard copy $($this.DestinationPath)" + $null = New-Item -ItemType Directory -Path $this.DestinationPath + } + + if ($this.SourcePath) + { + Write-Verbose -Message "Copying from $($this.SourcePath) to $($This.DestinationPath), Recurse is $($this.Recurse), Using the Force: $($this.Force)" + $copyParam = @{ + Path = $this.SourcePath + Destination = $this.DestinationPath + } + if ($this.Recurse) + { + $copyParam['Recurse'] = $this.Recurse + } + if ($this.Force) + { + $copyParam['Force'] = $this.Force + } + Copy-Item @copyParam + } + } + + [bool] Test() + { + $currentState = $this.Get() + + return ($currentState.Reasons.Count -eq 0) + } + + [System.IO.FileInfo[]] CompareHash([string]$Path, [string]$ReferencePath, [checksumType]$Type = 'md5', [bool]$Recurse) + { + [object[]]$sourceHashes = $this.GetHash($ReferencePath, $Type, $Recurse) + [object[]]$hashes = $this.GetHash($Path, $Type, $Recurse) + + if ($hashes.Count -eq 0) + { + return [System.IO.FileInfo[]]$sourceHashes.Path + } + + $comparison = Compare-Object -ReferenceObject $sourceHashes -DifferenceObject $hashes -Property Hash -PassThru | Where-Object SideIndicator -eq '<=' + return [System.IO.FileInfo[]]$comparison.Path + } + + # Return type unclear and either Microsoft.PowerShell.Commands.FileHashInfo or PSCustomObject + # Might be better to create a custom class for this + [object[]] GetHash([string]$Path, [checksumType]$Type, [bool]$Recurse) + { + $hashStrings = if ($Type -eq 'md5') + { + Get-ChildItem -Recurse:$Recurse -Path $Path -Force -File | Get-FileHash -Algorithm md5 + } + else + { + $propz = @( + @{ + Name = 'Path' + Expression = { $_.FullName } + } + @{ + Name = 'Algorithm' + Expression = { $Type } + } + @{ + Name = 'Hash' + Expression = { if ($Type -eq 'CreationTime') + { + $_.CreationTime + } + else + { + $_.LastWriteTime + } + } + } + ) + Get-ChildItem -Recurse:$Recurse -Path $Path -Force -File | Select-Object -Property $propz + } + + return $hashStrings + } +} +#EndRegion '.\Classes\002.FileSystemObject.ps1' 405 + diff --git a/output/RequiredModules/FileSystemDsc/1.1.0/Modules/DscResource.Common/0.16.0/DscResource.Common.psd1 b/output/RequiredModules/FileSystemDsc/1.1.0/Modules/DscResource.Common/0.16.0/DscResource.Common.psd1 new file mode 100644 index 00000000..cfdfffa2 --- /dev/null +++ b/output/RequiredModules/FileSystemDsc/1.1.0/Modules/DscResource.Common/0.16.0/DscResource.Common.psd1 @@ -0,0 +1,71 @@ +@{ + # Script module or binary module file associated with this manifest. + RootModule = 'DscResource.Common.psm1' + + # Version number of this module. + ModuleVersion = '0.16.0' + + # ID used to uniquely identify this module + GUID = '9c9daa5b-5c00-472d-a588-c96e8e498450' + + # Author of this module + Author = 'DSC Community' + + # Company or vendor of this module + CompanyName = 'DSC Community' + + # Copyright statement for this module + Copyright = 'Copyright the DSC Community contributors. All rights reserved.' + + # Description of the functionality provided by this module + Description = 'Common functions used in DSC Resources' + + # Minimum version of the PowerShell engine required by this module + PowerShellVersion = '4.0' + + # Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export. + FunctionsToExport = @('Assert-BoundParameter','Assert-ElevatedUser','Assert-IPAddress','Assert-Module','Compare-DscParameterState','Compare-ResourcePropertyState','ConvertFrom-DscResourceInstance','ConvertTo-CimInstance','ConvertTo-HashTable','Find-Certificate','Get-ComputerName','Get-DscProperty','Get-EnvironmentVariable','Get-LocalizedData','Get-LocalizedDataForInvariantCulture','Get-PSModulePath','Get-TemporaryFolder','New-InvalidArgumentException','New-InvalidDataException','New-InvalidOperationException','New-InvalidResultException','New-NotImplementedException','New-ObjectNotFoundException','Remove-CommonParameter','Set-DscMachineRebootRequired','Set-PSModulePath','Test-AccountRequirePassword','Test-DscParameterState','Test-DscProperty','Test-IsNanoServer','Test-IsNumericType') + + # Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export. + CmdletsToExport = @() + + # Variables to export from this module + VariablesToExport = @() + + # Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export. + AliasesToExport = @() + + # Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell. + PrivateData = @{ + + PSData = @{ + # Tags applied to this module. These help with module discovery in online galleries. + Tags = @('DSC', 'Localization') + + # A URL to the license for this module. + LicenseUri = 'https://github.com/dsccommunity/DscResource.Common/blob/main/LICENSE' + + # A URL to the main website for this project. + ProjectUri = 'https://github.com/dsccommunity/DscResource.Common' + + # A URL to an icon representing this module. + IconUri = 'https://dsccommunity.org/images/DSC_Logo_300p.png' + + # ReleaseNotes of this module + ReleaseNotes = '## [0.16.0] - 2023-04-10 + +### Added + +- New public commands. + - `Get-EnvironmentVariable` - Get a specific environment variable from a + specific environment variable target. + - `Get-PSModulePath` - Get the the PSModulePath from one or more environment + variable targets - [Issue #103](https://github.com/dsccommunity/DscResource.Common/issues/103) + +' + + Prerelease = '' + } # End of PSData hashtable + + } # End of PrivateData hashtable +} diff --git a/output/RequiredModules/FileSystemDsc/1.1.0/Modules/DscResource.Common/0.16.0/DscResource.Common.psm1 b/output/RequiredModules/FileSystemDsc/1.1.0/Modules/DscResource.Common/0.16.0/DscResource.Common.psm1 new file mode 100644 index 00000000..8e0f1f0e --- /dev/null +++ b/output/RequiredModules/FileSystemDsc/1.1.0/Modules/DscResource.Common/0.16.0/DscResource.Common.psm1 @@ -0,0 +1,3900 @@ +#Region './prefix.ps1' 0 +$script:modulesFolderPath = Split-Path -Path $PSScriptRoot -Parent +#EndRegion './prefix.ps1' 2 +#Region './Private/Assert-RequiredCommandParameter.ps1' 0 +<# + .SYNOPSIS + Assert that required parameters has been specified. + + .DESCRIPTION + Assert that required parameters has been specified, and throws an exception if not. + + .PARAMETER BoundParameterList + A hashtable containing the parameters to evaluate. Normally this is set to + $PSBoundParameters. + + .PARAMETER RequiredParameter + One or more parameter names that is required to have been specified. + + .PARAMETER IfParameterPresent + One or more parameter names that if specified will trigger the evaluation. + If neither of the parameter names has been specified the evaluation of required + parameters are not made. + + .EXAMPLE + Assert-RequiredCommandParameter -BoundParameter $PSBoundParameters -RequiredParameter @('PBStartPortRange', 'PBEndPortRange') + + Throws an exception if either of the two parameters are not specified. + + .EXAMPLE + Assert-RequiredCommandParameter -BoundParameter $PSBoundParameters -RequiredParameter @('Property2', 'Property3') -IfParameterPresent @('Property1') + + Throws an exception if the parameter 'Property1' is specified and either of the required parameters are not. + + .OUTPUTS + None. +#> +function Assert-RequiredCommandParameter +{ + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [System.Collections.Hashtable] + $BoundParameterList, + + [Parameter(Mandatory = $true)] + [System.String[]] + $RequiredParameter, + + [Parameter()] + [System.String[]] + $IfParameterPresent + ) + + $evaluateRequiredParameter = $true + + if ($PSBoundParameters.ContainsKey('IfParameterPresent')) + { + $hasIfParameterPresent = $BoundParameterList.Keys.Where( { $_ -in $IfParameterPresent } ) + + if (-not $hasIfParameterPresent) + { + $evaluateRequiredParameter = $false + } + } + + if ($evaluateRequiredParameter) + { + foreach ($parameter in $RequiredParameter) + { + if ($parameter -notin $BoundParameterList.Keys) + { + $errorMessage = if ($PSBoundParameters.ContainsKey('IfParameterPresent')) + { + $script:localizedData.RequiredCommandParameter_SpecificParametersMustAllBeSetWhenParameterExist -f ($RequiredParameter -join ''', '''), ($IfParameterPresent -join ''', ''') + } + else + { + $script:localizedData.RequiredCommandParameter_SpecificParametersMustAllBeSet -f ($RequiredParameter -join ''', ''') + } + + $PSCmdlet.ThrowTerminatingError( + [System.Management.Automation.ErrorRecord]::new( + $errorMessage, + 'ARCP0001', # cspell: disable-line + [System.Management.Automation.ErrorCategory]::InvalidOperation, + 'Command parameters' + ) + ) + } + } + } +} +#EndRegion './Private/Assert-RequiredCommandParameter.ps1' 90 +#Region './Private/Test-DscObjectHasProperty.ps1' 0 +<# + .SYNOPSIS + Tests if an object has a property. + + .DESCRIPTION + Tests if the specified object has the specified property and return + $true or $false. + + .PARAMETER Object + Specifies the object to test for the specified property. + + .PARAMETER PropertyName + Specifies the property name to test for. + + .EXAMPLE + Test-DscObjectHasProperty -Object 'AnyString' -PropertyName 'Length' +#> +function Test-DscObjectHasProperty +{ + [CmdletBinding()] + [OutputType([System.Boolean])] + param + ( + [Parameter(Mandatory = $true)] + [System.Object] + $Object, + + [Parameter(Mandatory = $true)] + [System.String] + $PropertyName + ) + + if ($Object.PSObject.Properties.Name -contains $PropertyName) + { + return [System.Boolean] $Object.$PropertyName + } + + return $false +} +#EndRegion './Private/Test-DscObjectHasProperty.ps1' 40 +#Region './Private/Test-DscPropertyIsAssigned.ps1' 0 +<# + .SYNOPSIS + Tests whether the class-based resource property is assigned a non-null value. + + .DESCRIPTION + Tests whether the class-based resource property is assigned a non-null value. + + .PARAMETER InputObject + Specifies the object that contain the property. + + .PARAMETER Name + Specifies the name of the property. + + .EXAMPLE + Test-DscPropertyIsAssigned -InputObject $this -Name 'MyDscProperty' + + Returns $true or $false whether the property is assigned or not. + + .OUTPUTS + [System.Boolean] + + .NOTES + This command only works with nullable data types, if using a non-nullable + type make sure to make it nullable, e.g. [nullable[System.Int32]]. +#> +function Test-DscPropertyIsAssigned +{ + [CmdletBinding()] + [OutputType([System.Boolean])] + param + ( + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] + [PSObject] + $InputObject, + + [Parameter(Mandatory = $true)] + [System.String] + $Name + ) + + begin + { + $isAssigned = $false + } + + process + { + $isAssigned = -not ($null -eq $InputObject.$Name) + } + + end + { + return $isAssigned + } +} +#EndRegion './Private/Test-DscPropertyIsAssigned.ps1' 56 +#Region './Private/Test-DscPropertyState.ps1' 0 +<# + .SYNOPSIS + Compares the current and the desired value of a property. + + .DESCRIPTION + This function is used to compare the current and the desired value of a + property. + + .PARAMETER Values + This is set to a hash table with the current value (the CurrentValue key) + and desired value (the DesiredValue key). + + .EXAMPLE + Test-DscPropertyState -Values @{ + CurrentValue = 'John' + DesiredValue = 'Alice' + } + + .EXAMPLE + Test-DscPropertyState -Values @{ + CurrentValue = 1 + DesiredValue = 2 + } + + .NOTES + This function is used by the cmdlet Compare-ResourcePropertyState. +#> +function Test-DscPropertyState +{ + [CmdletBinding()] + [OutputType([System.Boolean])] + param + ( + [Parameter(Mandatory = $true)] + [System.Collections.Hashtable] + $Values + ) + + if ($null -eq $Values.CurrentValue -and $null -eq $Values.DesiredValue) + { + # Both values are $null so return $true + $returnValue = $true + } + elseif ($null -eq $Values.CurrentValue -or $null -eq $Values.DesiredValue) + { + # Either CurrentValue or DesiredValue are $null so return $false + $returnValue = $false + } + elseif ( + $Values.DesiredValue -is [Microsoft.Management.Infrastructure.CimInstance[]] ` + -or $Values.DesiredValue -is [System.Array] -and $Values.DesiredValue[0] -is [Microsoft.Management.Infrastructure.CimInstance] + ) + { + if (-not $Values.ContainsKey('KeyProperties')) + { + $errorMessage = $script:localizedData.KeyPropertiesMissing + + New-InvalidOperationException -Message $errorMessage + } + + $propertyState = @() + + <# + It is a collection of CIM instances, then recursively call + Test-DscPropertyState for each CIM instance in the collection. + #> + foreach ($desiredCimInstance in $Values.DesiredValue) + { + $currentCimInstance = $Values.CurrentValue + + <# + Use the CIM instance Key properties to filter out the current + values if the exist. + #> + foreach ($keyProperty in $Values.KeyProperties) + { + $currentCimInstance = $currentCimInstance | + Where-Object -Property $keyProperty -EQ -Value $desiredCimInstance.$keyProperty + } + + if ($currentCimInstance.Count -gt 1) + { + $errorMessage = $script:localizedData.TooManyCimInstances + + New-InvalidOperationException -Message $errorMessage + } + + if ($currentCimInstance) + { + $keyCimInstanceProperties = $currentCimInstance.CimInstanceProperties | + Where-Object -FilterScript { + $_.Name -in $Values.KeyProperties + } + + <# + For each key property build a string representation of the + property name and its value. + #> + $keyPropertyValues = $keyCimInstanceProperties.ForEach({'{0}="{1}"' -f $_.Name, ($_.Value -join ',')}) + + Write-Debug -Message ( + $script:localizedData.TestingCimInstance -f @( + $currentCimInstance.CimClass.CimClassName, + ($keyPropertyValues -join ';') + ) + ) + } + else + { + $keyCimInstanceProperties = $desiredCimInstance.CimInstanceProperties | + Where-Object -FilterScript { + $_.Name -in $Values.KeyProperties + } + + <# + For each key property build a string representation of the + property name and its value. + #> + $keyPropertyValues = $keyCimInstanceProperties.ForEach({'{0}="{1}"' -f $_.Name, ($_.Value -join ',')}) + + Write-Debug -Message ( + $script:localizedData.MissingCimInstance -f @( + $desiredCimInstance.CimClass.CimClassName, + ($keyPropertyValues -join ';') + ) + ) + } + + # Recursively call Test-DscPropertyState with the CimInstance to evaluate. + $propertyState += Test-DscPropertyState -Values @{ + CurrentValue = $currentCimInstance + DesiredValue = $desiredCimInstance + } + } + + # Return $false if one property is found to not be in desired state. + $returnValue = -not ($false -in $propertyState) + } + elseif ($Values.DesiredValue -is [Microsoft.Management.Infrastructure.CimInstance]) + { + $propertyState = @() + + <# + It is a CIM instance, recursively call Test-DscPropertyState for each + CIM instance property. + #> + $desiredCimInstanceProperties = $Values.DesiredValue.CimInstanceProperties | + Select-Object -Property @('Name', 'Value') + + if ($desiredCimInstanceProperties) + { + foreach ($desiredCimInstanceProperty in $desiredCimInstanceProperties) + { + <# + Recursively call Test-DscPropertyState to evaluate each property + in the CimInstance. + #> + $propertyState += Test-DscPropertyState -Values @{ + CurrentValue = $Values.CurrentValue.($desiredCimInstanceProperty.Name) + DesiredValue = $desiredCimInstanceProperty.Value + } + } + } + else + { + if ($Values.CurrentValue.CimInstanceProperties.Count -gt 0) + { + # Current value did not have any CIM properties, but desired state has. + $propertyState += $false + } + } + + # Return $false if one property is found to not be in desired state. + $returnValue = -not ($false -in $propertyState) + } + elseif ($Values.DesiredValue -is [System.Array] -or $Values.CurrentValue -is [System.Array]) + { + $compareObjectParameters = @{ + ReferenceObject = $Values.CurrentValue + DifferenceObject = $Values.DesiredValue + } + + $arrayCompare = Compare-Object @compareObjectParameters + + if ($null -ne $arrayCompare) + { + Write-Debug -Message $script:localizedData.ArrayDoesNotMatch + + $arrayCompare | + ForEach-Object -Process { + if ($_.SideIndicator -eq '=>') + { + Write-Debug -Message ( + $script:localizedData.ArrayValueIsAbsent -f $_.InputObject + ) + } + else + { + Write-Debug -Message ( + $script:localizedData.ArrayValueIsPresent -f $_.InputObject + ) + } + } + + $returnValue = $false + } + else + { + $returnValue = $true + } + } + elseif ($Values.CurrentValue -ne $Values.DesiredValue) + { + $desiredType = $Values.DesiredValue.GetType() + + $returnValue = $false + + $supportedTypes = @( + 'String' + 'Int32' + 'UInt32' + 'Int16' + 'UInt16' + 'Single' + 'Boolean' + ) + + if ($desiredType.Name -notin $supportedTypes) + { + Write-Warning -Message ($script:localizedData.UnableToCompareType -f $desiredType.Name) + } + else + { + Write-Debug -Message ( + $script:localizedData.PropertyValueOfTypeDoesNotMatch ` + -f $desiredType.Name, $Values.CurrentValue, $Values.DesiredValue + ) + } + } + else + { + $returnValue = $true + } + + return $returnValue +} +#EndRegion './Private/Test-DscPropertyState.ps1' 247 +#Region './Public/Assert-BoundParameter.ps1' 0 +<# + .SYNOPSIS + Throws an error if there is a bound parameter that exists in both the + mutually exclusive lists. + + .DESCRIPTION + Throws an error if there is a bound parameter that exists in both the + mutually exclusive lists. + + .PARAMETER BoundParameterList + The parameters that should be evaluated against the mutually exclusive + lists MutuallyExclusiveList1 and MutuallyExclusiveList2. This parameter is + normally set to the $PSBoundParameters variable. + + .PARAMETER MutuallyExclusiveList1 + An array of parameter names that are not allowed to be bound at the + same time as those in MutuallyExclusiveList2. + + .PARAMETER MutuallyExclusiveList2 + An array of parameter names that are not allowed to be bound at the + same time as those in MutuallyExclusiveList1. + + .PARAMETER RequiredParameter + One or more parameter names that is required to have been specified. + + .PARAMETER IfParameterPresent + One or more parameter names that if specified will trigger the evaluation. + If neither of the parameter names has been specified the evaluation of required + parameters are not made. + + .EXAMPLE + $assertBoundParameterParameters = @{ + BoundParameterList = $PSBoundParameters + MutuallyExclusiveList1 = @( + 'Parameter1' + ) + MutuallyExclusiveList2 = @( + 'Parameter2' + ) + } + + Assert-BoundParameter @assertBoundParameterParameters + + This example throws an exception if `$PSBoundParameters` contains both + the parameters `Parameter1` and `Parameter2`. + + .EXAMPLE + Assert-BoundParameter -BoundParameterList $PSBoundParameters -RequiredParameter @('PBStartPortRange', 'PBEndPortRange') + + Throws an exception if either of the two parameters are not specified. + + .EXAMPLE + Assert-BoundParameter -BoundParameterList $PSBoundParameters -RequiredParameter @('Property2', 'Property3') -IfParameterPresent @('Property1') + + Throws an exception if the parameter 'Property1' is specified and either of the required parameters are not. +#> +function Assert-BoundParameter +{ + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [AllowEmptyCollection()] + [System.Collections.Hashtable] + $BoundParameterList, + + [Parameter(ParameterSetName = 'MutuallyExclusiveParameters', Mandatory = $true)] + [System.String[]] + $MutuallyExclusiveList1, + + [Parameter(ParameterSetName = 'MutuallyExclusiveParameters', Mandatory = $true)] + [System.String[]] + $MutuallyExclusiveList2, + + [Parameter(ParameterSetName = 'RequiredParameter', Mandatory = $true)] + [System.String[]] + $RequiredParameter, + + [Parameter(ParameterSetName = 'RequiredParameter')] + [System.String[]] + $IfParameterPresent + ) + + switch ($PSCmdlet.ParameterSetName) + { + 'MutuallyExclusiveParameters' + { + $itemFoundFromList1 = $BoundParameterList.Keys.Where({ $_ -in $MutuallyExclusiveList1 }) + $itemFoundFromList2 = $BoundParameterList.Keys.Where({ $_ -in $MutuallyExclusiveList2 }) + + if ($itemFoundFromList1.Count -gt 0 -and $itemFoundFromList2.Count -gt 0) + { + $errorMessage = ` + $script:localizedData.ParameterUsageWrong ` + -f ($MutuallyExclusiveList1 -join "','"), ($MutuallyExclusiveList2 -join "','") + + New-InvalidArgumentException -ArgumentName 'Parameters' -Message $errorMessage + } + + break + } + + 'RequiredParameter' + { + Assert-RequiredCommandParameter @PSBoundParameters + + break + } + } +} +#EndRegion './Public/Assert-BoundParameter.ps1' 111 +#Region './Public/Assert-ElevatedUser.ps1' 0 +<# + .SYNOPSIS + Assert that the user has elevated the PowerShell session. + + .DESCRIPTION + Assert that the user has elevated the PowerShell session. + + .EXAMPLE + Assert-ElevatedUser + + Throws an exception if the user has not elevated the PowerShell session. + + .OUTPUTS + None. +#> +function Assert-ElevatedUser +{ + [CmdletBinding()] + param () + + $isElevated = $false + + if ($IsMacOS -or $IsLinux) + { + $isElevated = (id -u) -eq 0 + } + else + { + [Security.Principal.WindowsPrincipal] $user = [Security.Principal.WindowsIdentity]::GetCurrent() + + $isElevated = $user.IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator) + } + + if (-not $isElevated) + { + $PSCmdlet.ThrowTerminatingError( + [System.Management.Automation.ErrorRecord]::new( + $script:localizedData.ElevatedUser_UserNotElevated, + 'UserNotElevated', + [System.Management.Automation.ErrorCategory]::InvalidOperation, + 'Command parameters' + ) + ) + } +} +#EndRegion './Public/Assert-ElevatedUser.ps1' 46 +#Region './Public/Assert-IPAddress.ps1' 0 +<# + .SYNOPSIS + Asserts that the specified IP address is valid. + + .DESCRIPTION + Checks the IP address so that it is valid and do not conflict with address + family. If any problems are detected an exception will be thrown. + + .PARAMETER AddressFamily + IP address family that the supplied Address should be in. Valid values are + 'IPv4' or 'IPv6'. + + .PARAMETER Address + Specifies an IPv4 or IPv6 address. + + .EXAMPLE + Assert-IPAddress -Address '127.0.0.1' + + This will assert that the supplied address is a valid IPv4 address. + If it is not an exception will be thrown. + + .EXAMPLE + Assert-IPAddress -Address 'fe80:ab04:30F5:002b::1' + + This will assert that the supplied address is a valid IPv6 address. + If it is not an exception will be thrown. + + .EXAMPLE + Assert-IPAddress -Address 'fe80:ab04:30F5:002b::1' -AddressFamily 'IPv6' + + This will assert that address is valid and that it matches the + supplied address family. If the supplied address family does not match + the address an exception will be thrown. +#> +function Assert-IPAddress +{ + [CmdletBinding()] + param + ( + [Parameter()] + [ValidateSet('IPv4', 'IPv6')] + [System.String] + $AddressFamily, + + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.String] + $Address + ) + + [System.Net.IPAddress] $ipAddress = $null + + if (-not ([System.Net.IPAddress]::TryParse($Address, [ref] $ipAddress))) + { + New-InvalidArgumentException ` + -Message ($script:localizedData.AddressFormatError -f $Address) ` + -ArgumentName 'Address' + } + + if ($AddressFamily) + { + switch ($AddressFamily) + { + 'IPv4' + { + if ($ipAddress.AddressFamily -ne [System.Net.Sockets.AddressFamily]::InterNetwork.ToString()) + { + New-InvalidArgumentException ` + -Message ($script:localizedData.AddressIPv6MismatchError -f $Address, $AddressFamily) ` + -ArgumentName 'AddressFamily' + } + } + + 'IPv6' + { + if ($ipAddress.AddressFamily -ne [System.Net.Sockets.AddressFamily]::InterNetworkV6.ToString()) + { + New-InvalidArgumentException ` + -Message ($script:localizedData.AddressIPv4MismatchError -f $Address, $AddressFamily) ` + -ArgumentName 'AddressFamily' + } + } + } + } +} +#EndRegion './Public/Assert-IPAddress.ps1' 86 +#Region './Public/Assert-Module.ps1' 0 +<# + .SYNOPSIS + Assert if the specific module is available to be imported. + + .DESCRIPTION + Assert if the specific module is available to be imported. + + .PARAMETER ModuleName + Specifies the name of the module to assert. + + .PARAMETER ImportModule + Specifies to import the module if it is asserted. + + .PARAMETER Force + Specifies to forcibly import the module even if it is already in the + session. This parameter is ignored unless parameter `ImportModule` is + also used. + + .EXAMPLE + Assert-Module -ModuleName 'DhcpServer' + + This asserts that the module DhcpServer is available on the system. + + .EXAMPLE + Assert-Module -ModuleName 'DhcpServer' -ImportModule + + This asserts that the module DhcpServer is available on the system and + imports it. + + .EXAMPLE + Assert-Module -ModuleName 'DhcpServer' -ImportModule -Force + + This asserts that the module DhcpServer is available on the system and + forcibly imports it. +#> +function Assert-Module +{ + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [System.String] + $ModuleName, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $ImportModule, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Force + ) + + <# + If the module is already in the session there is no need to use -ListAvailable. + This is a fix for issue #66. + #> + if (-not (Get-Module -Name $ModuleName)) + { + if (-not (Get-Module -Name $ModuleName -ListAvailable)) + { + $errorMessage = $script:localizedData.ModuleNotFound -f $ModuleName + New-ObjectNotFoundException -Message $errorMessage + } + + # Only import it here if $Force is not set, otherwise it will be imported below. + if ($ImportModule -and -not $Force) + { + Import-Module -Name $ModuleName + } + } + + # Always import the module even if already in session. + if ($ImportModule -and $Force) + { + Import-Module -Name $ModuleName -Force + } +} +#EndRegion './Public/Assert-Module.ps1' 79 +#Region './Public/Compare-DscParameterState.ps1' 0 +<# + .SYNOPSIS + This method is used to compare current and desired values for any DSC resource. + + .DESCRIPTION + This function compare the parameter status of DSC resource parameters against + the current values present on the system, and return a hashtable with the metadata + from the comparison. + + .PARAMETER CurrentValues + A hashtable with the current values on the system, obtained by e.g. + Get-TargetResource. + + .PARAMETER DesiredValues + The hashtable of desired values. For example $PSBoundParameters with the + desired values. + + .PARAMETER Properties + This is a list of properties in the desired values list should be checked. + If this is empty then all values in DesiredValues are checked. + + .PARAMETER ExcludeProperties + This is a list of which properties in the desired values list should be checked. + If this is empty then all values in DesiredValues are checked. + + .PARAMETER TurnOffTypeChecking + Indicates that the type of the parameter should not be checked. + + .PARAMETER ReverseCheck + Indicates that a reverse check should be done. The current and desired state + are swapped for another test. + + .PARAMETER SortArrayValues + If the sorting of array values does not matter, values are sorted internally + before doing the comparison. + + .PARAMETER IncludeInDesiredState + Indicates that result adds the properties in the desired state. + By default, this command returns only the properties not in desired state. + + .PARAMETER IncludeValue + Indicates that result contains the ActualValue and ExcpectedValue properties. + + .EXAMPLE + $currentValues = @{ + String = 'This is a string' + Int = 1 + Bool = $true + } + + $desiredValues = @{ + String = 'This is a string' + Int = 99 + } + + Compare-DscParameterState -CurrentValues $currentValues -DesiredValues $desiredValues + + Name Value + ---- ----- + Property Int + InDesiredState False + ExpectedType System.Int32 + ActualType System.Int32 + ``` + + The function Compare-DscParameterState compare the value of each hashtable based + on the keys present in $desiredValues hashtable. The result indicates that Int + property is not in the desired state. + No information about Bool property, because it is not in $desiredValues hashtable. + + .EXAMPLE + $currentValues = @{ + String = 'This is a string' + Int = 1 + Bool = $true + } + + $desiredValues = @{ + String = 'This is a string' + Int = 99 + Bool = $false + } + + $excludeProperties = @('Bool') + + Compare-DscParameterState ` + -CurrentValues $currentValues ` + -DesiredValues $desiredValues ` + -ExcludeProperties $ExcludeProperties + + Name Value + ---- ----- + Property Int + InDesiredState False + ExpectedType System.Int32 + ActualType System.Int32 + ``` + + The function Compare-DscParameterState compare the value of each hashtable based + on the keys present in $desiredValues hashtable and without those in $excludeProperties. + The result indicates that Int property is not in the desired state. + No information about Bool property, because it is in $excludeProperties. + + .EXAMPLE + $serviceParameters = @{ + Name = $Name + } + + $returnValue = Compare-DscParameterState ` + -CurrentValues (Get-Service @serviceParameters) ` + -DesiredValues $PSBoundParameters ` + -Properties @( + 'Name' + 'Status' + 'StartType' + ) + + This compares the values in the current state against the desires state. + The command Get-Service is called using just the required parameters + to get the values in the current state. The parameter 'Properties' + is used to specify the properties 'Name','Status' and + 'StartType' for the comparison. + +#> +function Compare-DscParameterState +{ + [CmdletBinding()] + [OutputType([System.Object[]])] + param + ( + [Parameter(Mandatory = $true)] + [System.Object] + $CurrentValues, + + [Parameter(Mandatory = $true)] + [System.Object] + $DesiredValues, + + [Parameter()] + [System.String[]] + [Alias('ValuesToCheck')] + $Properties, + + [Parameter()] + [System.String[]] + $ExcludeProperties, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $TurnOffTypeChecking, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $ReverseCheck, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $SortArrayValues, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $IncludeInDesiredState, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $IncludeValue + ) + + $returnValue = @() + #region ConvertCIm to Hashtable + if ($CurrentValues -is [Microsoft.Management.Infrastructure.CimInstance] -or + $CurrentValues -is [Microsoft.Management.Infrastructure.CimInstance[]]) + { + $CurrentValues = ConvertTo-HashTable -CimInstance $CurrentValues + } + + if ($DesiredValues -is [Microsoft.Management.Infrastructure.CimInstance] -or + $DesiredValues -is [Microsoft.Management.Infrastructure.CimInstance[]]) + { + $DesiredValues = ConvertTo-HashTable -CimInstance $DesiredValues + } + #endregion Endofconverion + #region CheckType of object + $types = 'System.Management.Automation.PSBoundParametersDictionary', + 'System.Collections.Hashtable', + 'Microsoft.Management.Infrastructure.CimInstance' + + if ($DesiredValues.GetType().FullName -notin $types) + { + New-InvalidArgumentException ` + -Message ($script:localizedData.InvalidDesiredValuesError -f $DesiredValues.GetType().FullName) ` + -ArgumentName 'DesiredValues' + } + + if ($CurrentValues.GetType().FullName -notin $types) + { + New-InvalidArgumentException ` + -Message ($script:localizedData.InvalidCurrentValuesError -f $CurrentValues.GetType().FullName) ` + -ArgumentName 'CurrentValues' + } + #endregion checktype + #region check if CimInstance and not have properties in parameters invoke exception + if ($DesiredValues -is [Microsoft.Management.Infrastructure.CimInstance] -and -not $Properties) + { + New-InvalidArgumentException ` + -Message $script:localizedData.InvalidPropertiesError ` + -ArgumentName Properties + } + #endregion check cim and properties + #Clean value if there are a common parameters provide from Test/Get-TargetResource parameter + $desiredValuesClean = Remove-CommonParameter -Hashtable $DesiredValues + #region generate keyList based on $Properties and $excludeProperties value + if (-not $Properties) + { + $keyList = $desiredValuesClean.Keys + } + else + { + $keyList = $Properties + } + + if ($ExcludeProperties) + { + $keyList = $keyList | Where-Object -FilterScript { $_ -notin $ExcludeProperties } + } + #endregion + #region enumerate of each key in list + foreach ($key in $keyList) + { + #generate default value + $InDesiredStateTable = [ordered]@{ + Property = $key + InDesiredState = $true + } + $returnValue += $InDesiredStateTable + #get value of each key + $desiredValue = $desiredValuesClean.$key + $currentValue = $CurrentValues.$key + + #Check if IncludeValue parameter is used. + if ($IncludeValue) + { + $InDesiredStateTable['ExpectedValue'] = $desiredValue + $InDesiredStateTable['ActualValue'] = $currentValue + } + + #region convert to hashtable if value of key is CimInstance + if ($desiredValue -is [Microsoft.Management.Infrastructure.CimInstance] -or + $desiredValue -is [Microsoft.Management.Infrastructure.CimInstance[]]) + { + $desiredValue = ConvertTo-HashTable -CimInstance $desiredValue + } + if ($currentValue -is [Microsoft.Management.Infrastructure.CimInstance] -or + $currentValue -is [Microsoft.Management.Infrastructure.CimInstance[]]) + { + $currentValue = ConvertTo-HashTable -CimInstance $currentValue + } + #endregion converttohashtable + #region gettype of value to check if they are the same. + if ($null -ne $desiredValue) + { + $desiredType = $desiredValue.GetType() + } + else + { + $desiredType = @{ + Name = 'Unknown' + } + } + + $InDesiredStateTable['ExpectedType'] = $desiredType + + if ($null -ne $currentValue) + { + $currentType = $currentValue.GetType() + } + else + { + $currentType = @{ + Name = 'Unknown' + } + } + + $InDesiredStateTable['ActualType'] = $currentType + + #endregion + #region check if the desiredtype if a credential object. Only if the current type isn't unknown. + if ($currentType.Name -ne 'Unknown' -and $desiredType.Name -eq 'PSCredential') + { + # This is a credential object. Compare only the user name + if ($currentType.Name -eq 'PSCredential' -and $currentValue.UserName -eq $desiredValue.UserName) + { + Write-Verbose -Message ($script:localizedData.MatchPsCredentialUsernameMessage -f $currentValue.UserName, $desiredValue.UserName) + continue # pass to the next key + } + elseif ($currentType.Name -ne 'string') + { + Write-Verbose -Message ($script:localizedData.NoMatchPsCredentialUsernameMessage -f $currentValue.UserName, $desiredValue.UserName) + $InDesiredStateTable.InDesiredState = $false + } + + # Assume the string is our username when the matching desired value is actually a credential + if ($currentType.Name -eq 'string' -and $currentValue -eq $desiredValue.UserName) + { + Write-Verbose -Message ($script:localizedData.MatchPsCredentialUsernameMessage -f $currentValue, $desiredValue.UserName) + continue # pass to the next key + } + else + { + Write-Verbose -Message ($script:localizedData.NoMatchPsCredentialUsernameMessage -f $currentValue, $desiredValue.UserName) + $InDesiredStateTable.InDesiredState = $false + } + } + #endregion test credential + #region Test type of object. And if they're not InDesiredState, generate en exception + if (-not $TurnOffTypeChecking) + { + if (($desiredType.Name -ne 'Unknown' -and $currentType.Name -ne 'Unknown') -and + $desiredType.FullName -ne $currentType.FullName) + { + Write-Verbose -Message ($script:localizedData.NoMatchTypeMismatchMessage -f $key, $currentType.FullName, $desiredType.FullName) + $InDesiredStateTable.InDesiredState = $false + continue # pass to the next key + } + elseif ($desiredType.Name -eq 'Unknown' -and $desiredType.Name -ne $currentType.Name) + { + Write-Verbose -Message ($script:localizedData.NoMatchTypeMismatchMessage -f $key, $currentType.Name, $desiredType.Name) + $InDesiredStateTable.InDesiredState = $false + continue # pass to the next key + } + } + #endregion TestType + #region Check if the value of Current and desired state is the same but only if they are not an array + if ($currentValue -eq $desiredValue -and -not $desiredType.IsArray) + { + Write-Verbose -Message ($script:localizedData.MatchValueMessage -f $desiredType.FullName, $key, $currentValue, $desiredValue) + continue # pass to the next key + } + #endregion check same value + #region Check if the DesiredValuesClean has the key and if it doesn't have, it's not necessary to check his value + if ($desiredValuesClean.GetType().Name -in 'HashTable', 'PSBoundParametersDictionary') + { + $checkDesiredValue = $desiredValuesClean.ContainsKey($key) + } + else + { + $checkDesiredValue = Test-DscObjectHasProperty -Object $desiredValuesClean -PropertyName $key + } + # if there no key, don't need to check + if (-not $checkDesiredValue) + { + Write-Verbose -Message ($script:localizedData.MatchValueMessage -f $desiredType.FullName, $key, $currentValue, $desiredValue) + continue # pass to the next key + } + #endregion + #region Check if desired type is array, ifno Hashtable and currenttype hashtable to + if ($desiredType.IsArray) + { + Write-Verbose -Message ($script:localizedData.TestDscParameterCompareMessage -f $key, $desiredType.FullName) + # Check if the currentValues and desiredValue are empty array. + if (-not $currentValue -and -not $desiredValue) + { + Write-Verbose -Message ($script:localizedData.MatchValueMessage -f $desiredType.FullName, $key, 'empty array', 'empty array') + continue + } + elseif (-not $currentValue) + { + #If only currentvalue is empty, the configuration isn't compliant. + Write-Verbose -Message ($script:localizedData.NoMatchValueMessage -f $desiredType.FullName, $key, $currentValue, $desiredValue) + $InDesiredStateTable.InDesiredState = $false + continue + } + elseif ($currentValue.Count -ne $desiredValue.Count) + { + #If there is a difference between the number of objects in arrays, this isn't compliant. + Write-Verbose -Message ($script:localizedData.NoMatchValueDifferentCountMessage -f $desiredType.FullName, $key, $currentValue.Count, $desiredValue.Count) + $InDesiredStateTable.InDesiredState = $false + continue + } + else + { + $desiredArrayValues = $desiredValue + $currentArrayValues = $currentValue + # if the sortArrayValues parameter is using, sort value of array + if ($SortArrayValues) + { + $desiredArrayValues = @($desiredArrayValues | Sort-Object) + $currentArrayValues = @($currentArrayValues | Sort-Object) + } + <# + for all object in collection, check their type.ConvertoString if they are script block. + + #> + for ($i = 0; $i -lt $desiredArrayValues.Count; $i++) + { + if ($desiredArrayValues[$i]) + { + $desiredType = $desiredArrayValues[$i].GetType() + } + else + { + $desiredType = @{ + Name = 'Unknown' + } + } + + if ($currentArrayValues[$i]) + { + $currentType = $currentArrayValues[$i].GetType() + } + else + { + $currentType = @{ + Name = 'Unknown' + } + } + + if (-not $TurnOffTypeChecking) + { + if (($desiredType.Name -ne 'Unknown' -and $currentType.Name -ne 'Unknown') -and + $desiredType.FullName -ne $currentType.FullName) + { + Write-Verbose -Message ($script:localizedData.NoMatchElementTypeMismatchMessage -f $key, $i, $currentType.FullName, $desiredType.FullName) + $InDesiredStateTable.InDesiredState = $false + continue + } + } + + <# + Convert a scriptblock into a string as scriptblocks are not comparable + if currentvalue is scriptblock and if desired value is string, + we invoke the result of script block. Ifno, we convert to string. + if Desired value + #> + + $wasCurrentArrayValuesConverted = $false + if ($currentArrayValues[$i] -is [scriptblock]) + { + $currentArrayValues[$i] = if ($desiredArrayValues[$i] -is [string]) + { + $currentArrayValues[$i] = $currentArrayValues[$i].Invoke() + } + else + { + $currentArrayValues[$i].ToString() + } + $wasCurrentArrayValuesConverted = $true + } + + if ($desiredArrayValues[$i] -is [scriptblock]) + { + $desiredArrayValues[$i] = if ($currentArrayValues[$i] -is [string] -and -not $wasCurrentArrayValuesConverted) + { + $desiredArrayValues[$i].Invoke() + } + else + { + $desiredArrayValues[$i].ToString() + } + } + + if ($desiredType -eq [System.Collections.Hashtable] -and $currentType -eq [System.Collections.Hashtable]) + { + $param = @{} + $PSBoundParameters + $param.CurrentValues = $currentArrayValues[$i] + $param.DesiredValues = $desiredArrayValues[$i] + + 'IncludeInDesiredState','IncludeValue','Properties','ReverseCheck' | ForEach-Object { + if ($param.ContainsKey($_)) + { + $null = $param.Remove($_) + } + } + + if ($InDesiredStateTable.InDesiredState) + { + $InDesiredStateTable.InDesiredState = Test-DscParameterState @param + } + else + { + Test-DscParameterState @param | Out-Null + } + continue + } + + if ($desiredArrayValues[$i] -ne $currentArrayValues[$i]) + { + Write-Verbose -Message ($script:localizedData.NoMatchElementValueMismatchMessage -f $i, $desiredType.FullName, $key, $currentArrayValues[$i], $desiredArrayValues[$i]) + $InDesiredStateTable.InDesiredState = $false + continue + } + else + { + Write-Verbose -Message ($script:localizedData.MatchElementValueMessage -f $i, $desiredType.FullName, $key, $currentArrayValues[$i], $desiredArrayValues[$i]) + continue + } + } + + } + } + elseif ($desiredType -eq [System.Collections.Hashtable] -and $currentType -eq [System.Collections.Hashtable]) + { + $param = @{} + $PSBoundParameters + $param.CurrentValues = $currentValue + $param.DesiredValues = $desiredValue + + 'IncludeInDesiredState','IncludeValue','Properties','ReverseCheck' | ForEach-Object { + if ($param.ContainsKey($_)) + { + $null = $param.Remove($_) + } + } + + if ($InDesiredStateTable.InDesiredState) + { + <# + if desiredvalue is an empty hashtable and not currentvalue, it's not necessery to compare them, it's not compliant. + See issue 65 https://github.com/dsccommunity/DscResource.Common/issues/65 + #> + if ($desiredValue.Keys.Count -eq 0 -and $currentValue.Keys.Count -ne 0) + { + Write-Verbose -Message ($script:localizedData.NoMatchKeyMessage -f $desiredType.FullName, $key, $($currentValue.Keys -join ', ')) + $InDesiredStateTable.InDesiredState = $false + } + else{ + $InDesiredStateTable.InDesiredState = Test-DscParameterState @param + } + } + else + { + $null = Test-DscParameterState @param + } + continue + } + else + { + #Convert a scriptblock into a string as scriptblocks are not comparable + $wasCurrentValue = $false + if ($currentValue -is [scriptblock]) + { + $currentValue = if ($desiredValue -is [string]) + { + $currentValue = $currentValue.Invoke() + } + else + { + $currentValue.ToString() + } + $wasCurrentValue = $true + } + if ($desiredValue -is [scriptblock]) + { + $desiredValue = if ($currentValue -is [string] -and -not $wasCurrentValue) + { + $desiredValue.Invoke() + } + else + { + $desiredValue.ToString() + } + } + + if ($desiredValue -ne $currentValue) + { + Write-Verbose -Message ($script:localizedData.NoMatchValueMessage -f $desiredType.FullName, $key, $currentValue, $desiredValue) + $InDesiredStateTable.InDesiredState = $false + } + } + #endregion check type + } + #endregion end of enumeration + if ($ReverseCheck) + { + Write-Verbose -Message $script:localizedData.StartingReverseCheck + $reverseCheckParameters = @{} + $PSBoundParameters + $reverseCheckParameters['CurrentValues'] = $DesiredValues + $reverseCheckParameters['DesiredValues'] = $CurrentValues + $reverseCheckParameters['Properties'] = $keyList + $CurrentValues.Keys | Select-Object -Unique + if ($ExcludeProperties) + { + $reverseCheckParameters['Properties'] = $reverseCheckParameters['Properties'] | Where-Object -FilterScript { $_ -notin $ExcludeProperties } + } + + $null = $reverseCheckParameters.Remove('ReverseCheck') + + if ($returnValue) + { + $returnValue = Compare-DscParameterState @reverseCheckParameters + } + else + { + $null = Compare-DscParameterState @reverseCheckParameters + } + } + + # Remove in desired state value if IncludeDesirateState parameter is not use + if (-not $IncludeInDesiredState) + { + [array]$returnValue = $returnValue.where({$_.InDesiredState -eq $false}) + } + + #change verbose message + if ($IncludeInDesiredState.IsPresent) + { + $returnValue.ForEach({ + if ($_.InDesiredState) + { + $localizedString = $script:localizedData.PropertyInDesiredStateMessage + } + else + { + $localizedString = $script:localizedData.PropertyNotInDesiredStateMessage + } + + Write-Verbose -Message ($localizedString -f $_.Property) + }) + } + <# + If Compare-DscParameterState is used in precedent step, don't need to convert it + We use .foreach() method as we are sure that $returnValue is an array. + #> + [Array]$returnValue = @( + $returnValue.foreach( + { + if ($_ -is [System.Collections.Hashtable]) + { + [pscustomobject]$_ + } + else + { + $_ + } + } + ) + ) + + return $returnValue +} +#EndRegion './Public/Compare-DscParameterState.ps1' 639 +#Region './Public/Compare-ResourcePropertyState.ps1' 0 +<# + .SYNOPSIS + Compare current and desired property values for any DSC resource. + + .DESCRIPTION + This function is used to compare current and desired property values for any + DSC resource, and return a hashtable with the metadata from the comparison. + + .PARAMETER CurrentValues + The current values that should be compared to to desired values. Normally + the values returned from Get-TargetResource. + + .PARAMETER DesiredValues + The values set in the configuration and is provided in the call to the + functions *-TargetResource, and that will be compared against current + values. Normally set to $PSBoundParameters. + + .PARAMETER Properties + An array of property names, from the keys provided in DesiredValues, that + will be compared. If this parameter is left out, all the keys in the + DesiredValues will be compared. + + .PARAMETER IgnoreProperties + An array of property names, from the keys provided in DesiredValues, that + will be ignored in the comparison. If this parameter is left out, all the + keys in the DesiredValues will be compared. + + .PARAMETER CimInstanceKeyProperties + A hashtable containing a key for each property that contain a collection + of CimInstances and the value is an array of strings of the CimInstance + key properties. + @{ + Permission = @('State') + } + + .EXAMPLE + $compareTargetResourceStateParameters = @{ + CurrentValues = (Get-TargetResource $PSBoundParameters) + DesiredValues = $PSBoundParameters + } + + $propertyState = Compare-ResourcePropertyState @compareTargetResourceStateParameters + + This examples call Compare-ResourcePropertyState with the current state + and the desired state and returns a hashtable array of all the properties + that was evaluated based on the properties pass in the parameter DesiredValues. +#> +function Compare-ResourcePropertyState +{ + [CmdletBinding()] + [OutputType([System.Collections.Hashtable[]])] + param + ( + [Parameter(Mandatory = $true)] + [System.Collections.Hashtable] + $CurrentValues, + + [Parameter(Mandatory = $true)] + [System.Collections.Hashtable] + $DesiredValues, + + [Parameter()] + [ValidateNotNullOrEmpty()] + [System.String[]] + $Properties, + + [Parameter()] + [ValidateNotNullOrEmpty()] + [System.String[]] + $IgnoreProperties, + + [Parameter()] + [ValidateNotNull()] + [System.Collections.Hashtable] + $CimInstanceKeyProperties = @{} + ) + + if ($PSBoundParameters.ContainsKey('Properties')) + { + # Filter out the parameters (keys) not specified in Properties + $desiredValuesToRemove = $DesiredValues.Keys | + Where-Object -FilterScript { + $_ -notin $Properties + } + + $desiredValuesToRemove | + ForEach-Object -Process { + $DesiredValues.Remove($_) + } + } + else + { + <# + Remove any common parameters that might be part of DesiredValues, + if it $PSBoundParameters was used to pass the desired values. + #> + $commonParametersToRemove = $DesiredValues.Keys | + Where-Object -FilterScript { + $_ -in [System.Management.Automation.PSCmdlet]::CommonParameters ` + -or $_ -in [System.Management.Automation.PSCmdlet]::OptionalCommonParameters + } + + $commonParametersToRemove | + ForEach-Object -Process { + $DesiredValues.Remove($_) + } + } + + # Remove any properties that should be ignored. + if ($PSBoundParameters.ContainsKey('IgnoreProperties')) + { + $IgnoreProperties | + ForEach-Object -Process { + if ($DesiredValues.ContainsKey($_)) + { + $DesiredValues.Remove($_) + } + } + } + + $compareTargetResourceStateReturnValue = @() + + foreach ($parameterName in $DesiredValues.Keys) + { + Write-Debug -Message ($script:localizedData.EvaluatePropertyState -f $parameterName) + + $parameterState = @{ + ParameterName = $parameterName + Expected = $DesiredValues.$parameterName + Actual = $CurrentValues.$parameterName + } + + # Check if the parameter is in compliance. + $isPropertyInDesiredState = Test-DscPropertyState -Values @{ + CurrentValue = $CurrentValues.$parameterName + DesiredValue = $DesiredValues.$parameterName + KeyProperties = $CimInstanceKeyProperties.$parameterName + } + + if ($isPropertyInDesiredState) + { + Write-Verbose -Message ($script:localizedData.PropertyInDesiredState -f $parameterName) + + $parameterState['InDesiredState'] = $true + } + else + { + Write-Verbose -Message ($script:localizedData.PropertyNotInDesiredState -f $parameterName) + + $parameterState['InDesiredState'] = $false + } + + $compareTargetResourceStateReturnValue += $parameterState + } + + return $compareTargetResourceStateReturnValue +} +#EndRegion './Public/Compare-ResourcePropertyState.ps1' 158 +#Region './Public/ConvertFrom-DscResourceInstance.ps1' 0 + +<# + .SYNOPSIS + Convert any object to hashtable. + + .DESCRIPTION + This function is used to convert a psobject into a hashtable. + + .PARAMETER InputObject + The object that should be convert to hashtable. + + .PARAMETER OutPutFormat + Set the format you do want to convert the object. The default value is HashTable. + It's the only value accepted at this time. + + .OUTPUTS + Hashtable + + .EXAMPLE + + $Object = [pscustomobject]=@{ + FirstName = 'John' + LastName = 'Smith' + } + + ConvertFrom-DscResourceInstance -InputObject $Object + + This creates a pscustomobject and converts its properties/values to Hashtable Key/Value. + + .EXAMPLE + + $ObjectArray = [pscustomobject]=@{ + FirstName = 'John' + LastName = 'Smith' + },[pscustomobject]=@{ + FirstName = 'Peter' + LastName = 'Smith' + } + + $ObjectArray | ConvertFrom-DscResourceInstance + + This creates pscustomobjects and converts there properties/values to Hashtable Keys/Values through the pipeline. +#> +function ConvertFrom-DscResourceInstance +{ + [CmdletBinding()] + [OutputType([Hashtable])] + param + ( + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] + [PSObject] + $InputObject, + + [Parameter()] + [ValidateSet('HashTable')] + [String] + $OutPutFormat = 'HashTable' + + ) + process { + + switch ($OutPutFormat) + { + 'HashTable' + { + $Result = @{} + foreach ($obj in $InputObject) + { + $obj.psobject.Properties | Foreach-Object { + $Result[$_.Name] = $_.Value + } + } + } + } + + return $Result + } +} +#EndRegion './Public/ConvertFrom-DscResourceInstance.ps1' 79 +#Region './Public/ConvertTo-CimInstance.ps1' 0 +<# + .SYNOPSIS + Converts a hashtable into a CimInstance array. + + .DESCRIPTION + This function is used to convert a hashtable into MSFT_KeyValuePair objects. + These are stored as an CimInstance array. DSC cannot handle hashtables but + CimInstances arrays storing MSFT_KeyValuePair. + + .PARAMETER Hashtable + A hashtable with the values to convert. + + .OUTPUTS + An object array with CimInstance objects. + + .EXAMPLE + ConvertTo-CimInstance -Hashtable @{ + String = 'a string' + Bool = $true + Int = 99 + Array = 'a, b, c' + } + + This example returns an CimInstance with the provided hashtable values. +#> +function ConvertTo-CimInstance +{ + [CmdletBinding()] + [OutputType([System.Object[]])] + param + ( + [Parameter(Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = 'Hashtable')] + [System.Collections.Hashtable] + $Hashtable + ) + + process + { + foreach ($item in $Hashtable.GetEnumerator()) + { + New-CimInstance -ClassName 'MSFT_KeyValuePair' -Namespace 'root/microsoft/Windows/DesiredStateConfiguration' -Property @{ + Key = $item.Key + Value = if ($item.Value -is [array]) + { + $item.Value -join ',' + } + else + { + $item.Value + } + } -ClientOnly + } + } +} +#EndRegion './Public/ConvertTo-CimInstance.ps1' 55 +#Region './Public/ConvertTo-HashTable.ps1' 0 +<# + .SYNOPSIS + Converts CimInstances into a hashtable. + + .DESCRIPTION + This function is used to convert a CimInstance array containing + MSFT_KeyValuePair objects into a hashtable. + + .PARAMETER CimInstance + An array of CimInstances or a single CimInstance object to convert. + + .OUTPUTS + Hashtable + + .EXAMPLE + $newInstanceParameters = @{ + ClassName = 'MSFT_KeyValuePair' + Namespace = 'root/microsoft/Windows/DesiredStateConfiguration' + ClientOnly = $true + } + + $cimInstance = [Microsoft.Management.Infrastructure.CimInstance[]] ( + (New-CimInstance @newInstanceParameters -Property @{ + Key = 'FirstName' + Value = 'John' + }), + + (New-CimInstance @newInstanceParameters -Property @{ + Key = 'LastName' + Value = 'Smith' + }) + ) + + ConvertTo-HashTable -CimInstance $cimInstance + + This creates a array om CimInstances of the class name MSFT_KeyValuePair + and passes it to ConvertTo-HashTable which returns a hashtable. +#> +function ConvertTo-HashTable +{ + [CmdletBinding()] + [OutputType([System.Collections.Hashtable])] + param + ( + [Parameter(Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = 'CimInstance')] + [AllowEmptyCollection()] + [Microsoft.Management.Infrastructure.CimInstance[]] + $CimInstance + ) + + begin + { + $result = @{ } + } + + process + { + foreach ($ci in $CimInstance) + { + $result.Add($ci.Key, $ci.Value) + } + } + + end + { + $result + } +} +#EndRegion './Public/ConvertTo-HashTable.ps1' 69 +#Region './Public/Find-Certificate.ps1' 0 +<# + .SYNOPSIS + Locates one or more certificates using the passed certificate selector parameters. + + If more than one certificate is found matching the selector criteria, they will be + returned in order of descending expiration date. + + .DESCRIPTION + A common function to find certificates based on multiple search filters, including, + but not limited to: Thumbprint, Friendly Name, DNS Names, Key Usage, Issuers, etc. + + .PARAMETER Thumbprint + The thumbprint of the certificate to find. + + .PARAMETER FriendlyName + The friendly name of the certificate to find. + + .PARAMETER Subject + The subject of the certificate to find. + + .PARAMETER DNSName + The subject alternative name of the certificate to export must contain these values. + + .PARAMETER Issuer + The issuer of the certificate to find. + + .PARAMETER KeyUsage + The key usage of the certificate to find must contain these values. + + .PARAMETER EnhancedKeyUsage + The enhanced key usage of the certificate to find must contain these values. + + .PARAMETER Store + The Windows Certificate Store Name to search for the certificate in. + Defaults to 'My'. + + .PARAMETER AllowExpired + Allows expired certificates to be returned. + + .EXAMPLE + Find-Certificate -Thumbprint '1111111111111111111111111111111111111111' + + Return certificate that matches thumbprint. + + .EXAMPLE + Find-Certificate -KeyUsage 'DataEncipherment', 'DigitalSignature' + + Return certificate(s) that have specific key usage. + + .EXAMPLE + Find-Certificate -DNSName 'www.fabrikam.com', 'www.contoso.com' + + Return certificate(s) filtered on specific DNS Names. + + .EXAMPLE + find-certificate -Subject 'CN=contoso, DC=com' + + Return certificate(s) with specific subject. + + .EXAMPLE + find-certificate -Issuer 'CN=contoso-ca, DC=com' -AllowExpired $true + + Return all certificates from specific issuer, including expired certificates. + + .EXAMPLE + Find-Certificate -EnhancedKeyUsage 'Server Authentication' -AllowExpired $true + + Return all certificates that can be used for "Server Authentication", including expired certificates. + + .EXAMPLE + Find-Certificate -FriendlyName 'My IIS Site SSL Cert' + + Return certificate based on FriendlyName. + +#> +function Find-Certificate +{ + [CmdletBinding()] + [OutputType([System.Security.Cryptography.X509Certificates.X509Certificate2[]])] + param + ( + [Parameter()] + [System.String] + $Thumbprint, + + [Parameter()] + [System.String] + $FriendlyName, + + [Parameter()] + [System.String] + $Subject, + + [Parameter()] + [System.String[]] + $DNSName, + + [Parameter()] + [System.String] + $Issuer, + + [Parameter()] + [System.String[]] + $KeyUsage, + + [Parameter()] + [System.String[]] + $EnhancedKeyUsage, + + [Parameter()] + [System.String] + $Store = 'My', + + [Parameter()] + [Boolean] + $AllowExpired = $false + ) + + $certPath = Join-Path -Path 'Cert:\LocalMachine' -ChildPath $Store + + if (-not (Test-Path -Path $certPath)) + { + # The Certificte Path is not valid + New-InvalidArgumentException ` + -Message ($script:localizedData.CertificatePathError -f $certPath) ` + -ArgumentName 'Store' + } # if + + # Assemble the filter to use to select the certificate + $certFilters = @() + + if ($PSBoundParameters.ContainsKey('Thumbprint')) + { + $certFilters += @('($_.Thumbprint -eq $Thumbprint)') + } # if + + if ($PSBoundParameters.ContainsKey('FriendlyName')) + { + $certFilters += @('($_.FriendlyName -eq $FriendlyName)') + } # if + + if ($PSBoundParameters.ContainsKey('Subject')) + { + $certFilters += @('($_.Subject -eq $Subject)') + } # if + + if ($PSBoundParameters.ContainsKey('Issuer')) + { + $certFilters += @('($_.Issuer -eq $Issuer)') + } # if + + if (-not $AllowExpired) + { + $certFilters += @('(((Get-Date) -le $_.NotAfter) -and ((Get-Date) -ge $_.NotBefore))') + } # if + + if ($PSBoundParameters.ContainsKey('DNSName')) + { + $certFilters += @('(@(Compare-Object -ReferenceObject $_.DNSNameList.Unicode -DifferenceObject $DNSName | Where-Object -Property SideIndicator -eq "=>").Count -eq 0)') + } # if + + if ($PSBoundParameters.ContainsKey('KeyUsage')) + { + $certFilters += @('(@(Compare-Object -ReferenceObject ($_.Extensions.KeyUsages -split ", ") -DifferenceObject $KeyUsage | Where-Object -Property SideIndicator -eq "=>").Count -eq 0)') + } # if + + if ($PSBoundParameters.ContainsKey('EnhancedKeyUsage')) + { + $certFilters += @('(@(Compare-Object -ReferenceObject ($_.EnhancedKeyUsageList.FriendlyName) -DifferenceObject $EnhancedKeyUsage | Where-Object -Property SideIndicator -eq "=>").Count -eq 0)') + } # if + + # Join all the filters together + $certFilterScript = '(' + ($certFilters -join ' -and ') + ')' + + Write-Verbose ` + -Message ($script:localizedData.SearchingForCertificateUsingFilters -f $store, $certFilterScript) ` + -Verbose + + $certs = Get-ChildItem -Path $certPath | + Where-Object -FilterScript ([ScriptBlock]::Create($certFilterScript)) + + # Sort the certificates + if ($certs.count -gt 1) + { + $certs = $certs | Sort-Object -Descending -Property 'NotAfter' + } # if + + return $certs +} # end function Find-Certificate +#EndRegion './Public/Find-Certificate.ps1' 190 +#Region './Public/Get-ComputerName.ps1' 0 +<# + .SYNOPSIS + Returns the computer name cross-plattform. + + .DESCRIPTION + The variable `$env:COMPUTERNAME` does not exist cross-platform which + hinders development and testing on macOS and Linux. Instead this cmdlet + can be used to get the computer name cross-plattform. + + .EXAMPLE + Get-ComputerName + + This example returns the computer name cross-plattform. +#> +function Get-ComputerName +{ + [CmdletBinding()] + [OutputType([System.String])] + param () + + $computerName = $null + + if ($IsLinux -or $IsMacOs) + { + $computerName = hostname + } + else + { + <# + We could run 'hostname' on Windows too, but $env:COMPUTERNAME + is more widely used. + #> + $computerName = $env:COMPUTERNAME + } + + return $computerName +} +#EndRegion './Public/Get-ComputerName.ps1' 38 +#Region './Public/Get-DscProperty.ps1' 0 + +<# + .SYNOPSIS + Returns DSC resource properties that is part of a class-based DSC resource. + + .DESCRIPTION + Returns DSC resource properties that is part of a class-based DSC resource. + The properties can be filtered using name, attribute, or if it has been + assigned a value. + + .PARAMETER InputObject + The object that contain one or more key properties. + + .PARAMETER Name + Specifies one or more property names to return. If left out all properties + are returned. + + .PARAMETER ExcludeName + Specifies one or more property names to exclude. + + .PARAMETER Attribute + Specifies one or more property attributes to return. If left out all property + types are returned. + + .PARAMETER HasValue + Specifies to return only properties that has been assigned a non-null value. + If left out all properties are returned regardless if there is a value + assigned or not. + + .EXAMPLE + Get-DscProperty -InputObject $this + + Returns all DSC resource properties of the DSC resource. + + .EXAMPLE + $this | Get-DscProperty + + Returns all DSC resource properties of the DSC resource. + + .EXAMPLE + Get-DscProperty -InputObject $this -Name @('MyProperty1', 'MyProperty2') + + Returns the DSC resource properties with the specified names. + + .EXAMPLE + Get-DscProperty -InputObject $this -Attribute @('Mandatory', 'Optional') + + Returns the DSC resource properties that has the specified attributes. + + .EXAMPLE + Get-DscProperty -InputObject $this -Attribute @('Optional') -HasValue + + Returns the DSC resource properties that has the specified attributes and + has a non-null value assigned. + + .OUTPUTS + [System.Collections.Hashtable] + + .NOTES + This command only works with nullable data types, if using a non-nullable + type make sure to make it nullable, e.g. [Nullable[System.Int32]]. +#> +function Get-DscProperty +{ + [CmdletBinding()] + [OutputType([System.Collections.Hashtable])] + param + ( + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] + [PSObject] + $InputObject, + + [Parameter()] + [System.String[]] + $Name, + + [Parameter()] + [System.String[]] + $ExcludeName, + + [Parameter()] + [ValidateSet('Key', 'Mandatory', 'NotConfigurable', 'Optional')] + [Alias('Type')] + [System.String[]] + $Attribute, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $HasValue + ) + + process + { + $property = $InputObject.PSObject.Properties.Name | + Where-Object -FilterScript { + <# + Return all properties if $Name is not assigned, or if assigned + just those properties. + #> + (-not $Name -or $_ -in $Name) -and + + <# + Return all properties if $ExcludeName is not assigned. Skip + property if it is included in $ExcludeName. + #> + (-not $ExcludeName -or ($_ -notin $ExcludeName)) -and + + # Only return the property if it is a DSC property. + $InputObject.GetType().GetMember($_).CustomAttributes.Where( + { + $_.AttributeType.Name -eq 'DscPropertyAttribute' + } + ) + } + + if (-not [System.String]::IsNullOrEmpty($property)) + { + if ($PSBoundParameters.ContainsKey('Attribute')) + { + $propertiesOfAttribute = @() + + $propertiesOfAttribute += $property | Where-Object -FilterScript { + $InputObject.GetType().GetMember($_).CustomAttributes.Where( + { + <# + To simplify the code, ignoring that this will compare + MemberNAme against type 'Optional' which does not exist. + #> + $_.NamedArguments.MemberName -in $Attribute + } + ).NamedArguments.TypedValue.Value -eq $true + } + + # Include all optional parameter if it was requested. + if ($Attribute -contains 'Optional') + { + $propertiesOfAttribute += $property | Where-Object -FilterScript { + $InputObject.GetType().GetMember($_).CustomAttributes.Where( + { + $_.NamedArguments.MemberName -notin @('Key', 'Mandatory', 'NotConfigurable') + } + ) + } + } + + $property = $propertiesOfAttribute + } + } + + # Return a hashtable containing each key property and its value. + $getPropertyResult = @{} + + foreach ($currentProperty in $property) + { + if ($HasValue.IsPresent) + { + $isAssigned = Test-DscPropertyIsAssigned -Name $currentProperty -InputObject $InputObject + + if (-not $isAssigned) + { + continue + } + } + + $getPropertyResult.$currentProperty = $InputObject.$currentProperty + } + + return $getPropertyResult + } +} +#EndRegion './Public/Get-DscProperty.ps1' 171 +#Region './Public/Get-EnvironmentVariable.ps1' 0 + +<# + .SYNOPSIS + Returns the value from an environment variable from a specified target. + + .DESCRIPTION + Returns the value from an environment variable from a specified target. + + .PARAMETER Name + Specifies the environment variable name. + + .PARAMETER FromTarget + Specifies the target to return the value from. Defaults to 'Session'. + + .EXAMPLE + Get-EnvironmentVariable -Name 'PSModulePath' + + Returns the value for the environment variable PSModulePath. + + .EXAMPLE + Get-EnvironmentVariable -Name 'PSModulePath' -FromTarget 'Machine' + + Returns the value for the environment variable PSModulePath from the + Machine target. + + .OUTPUTS + [System.String] +#> +function Get-EnvironmentVariable +{ + [CmdletBinding()] + [OutputType([System.String])] + param + ( + [Parameter(Mandatory = $true)] + [System.String] + $Name, + + [Parameter()] + [ValidateSet('Session', 'User', 'Machine')] + [System.String] + $FromTarget = 'Session' + ) + + switch ($FromTarget) + { + 'Session' + { + $value = [System.Environment]::GetEnvironmentVariable($Name) + } + + 'User' + { + $value = [System.Environment]::GetEnvironmentVariable($Name, 'User') + } + + 'Machine' + { + $value = [System.Environment]::GetEnvironmentVariable($Name, 'Machine') + } + } + + return $value +} +#EndRegion './Public/Get-EnvironmentVariable.ps1' 65 +#Region './Public/Get-LocalizedData.ps1' 0 + +<# + .SYNOPSIS + Gets language-specific data into scripts and functions based on the UI culture + that is selected for the operating system. + Similar to Import-LocalizedData, with extra parameter 'DefaultUICulture'. + + .DESCRIPTION + The Get-LocalizedData cmdlet dynamically retrieves strings from a subdirectory + whose name matches the UI language set for the current user of the operating system. + It is designed to enable scripts to display user messages in the UI language selected + by the current user. + + Get-LocalizedData imports data from .psd1 files in language-specific subdirectories + of the script directory and saves them in a local variable that is specified in the + command. The cmdlet selects the subdirectory and file based on the value of the + $PSUICulture automatic variable. When you use the local variable in the script to + display a user message, the message appears in the user's UI language. + + You can use the parameters of G-LocalizedData to specify an alternate UI culture, + path, and file name, to add supported commands, and to suppress the error message that + appears if the .psd1 files are not found. + + The G-LocalizedData cmdlet supports the script internationalization + initiative that was introduced in Windows PowerShell 2.0. This initiative + aims to better serve users worldwide by making it easy for scripts to display + user messages in the UI language of the current user. For more information + about this and about the format of the .psd1 files, see about_Script_Internationalization. + + .PARAMETER BindingVariable + Specifies the variable into which the text strings are imported. Enter a variable + name without a dollar sign ($). + + In Windows PowerShell 2.0, this parameter is required. In Windows PowerShell 3.0, + this parameter is optional. If you omit this parameter, Import-LocalizedData + returns a hash table of the text strings. The hash table is passed down the pipeline + or displayed at the command line. + + When using Import-LocalizedData to replace default text strings specified in the + DATA section of a script, assign the DATA section to a variable and enter the name + of the DATA section variable in the value of the BindingVariable parameter. Then, + when Import-LocalizedData saves the imported content in the BindingVariable, the + imported data will replace the default text strings. If you are not specifying + default text strings, you can select any variable name. + + .PARAMETER UICulture + Specifies an alternate UI culture. The default is the value of the $PsUICulture + automatic variable. Enter a UI culture in - format, such as + en-US, de-DE, or ar-SA. + + The value of the UICulture parameter determines the language-specific subdirectory + (within the base directory) from which Import-LocalizedData gets the .psd1 file + for the script. + + The cmdlet searches for a subdirectory with the same name as the value of the + UICulture parameter or the $PsUICulture automatic variable, such as de-DE or + ar-SA. If it cannot find the directory, or the directory does not contain a .psd1 + file for the script, it searches for a subdirectory with the name of the language + code, such as de or ar. If it cannot find the subdirectory or .psd1 file, the + command fails and the data is displayed in the default language specified in the + script. + + .PARAMETER BaseDirectory + Specifies the base directory where the .psd1 files are located. The default is + the directory where the script is located. Import-LocalizedData searches for + the .psd1 file for the script in a language-specific subdirectory of the base + directory. + + .PARAMETER FileName + Specifies the name of the data file (.psd1) to be imported. Enter a file name. + You can specify a file name that does not include its .psd1 file name extension, + or you can specify the file name including the .psd1 file name extension. + + The FileName parameter is required when Import-LocalizedData is not used in a + script. Otherwise, the parameter is optional and the default value is the base + name of the script. You can use this parameter to direct Import-LocalizedData + to search for a different .psd1 file. + + For example, if the FileName is omitted and the script name is FindFiles.ps1, + Import-LocalizedData searches for the FindFiles.psd1 data file. + + .PARAMETER SupportedCommand + Specifies cmdlets and functions that generate only data. + + Use this parameter to include cmdlets and functions that you have written or + tested. For more information, see about_Script_Internationalization. + + .PARAMETER DefaultUICulture + Specifies which UICulture to default to if current UI culture or its parents + culture don't have matching data file. + + For example, if you have a data file in 'en-US' but not in 'en' or 'en-GB' and + your current culture is 'en-GB', you can default back to 'en-US'. + + .NOTES + Before using Import-LocalizedData, localize your user messages. Format the messages + for each locale (UI culture) in a hash table of key/value pairs, and save the + hash table in a file with the same name as the script and a .psd1 file name extension. + Create a directory under the script directory for each supported UI culture, and + then save the .psd1 file for each UI culture in the directory with the UI + culture name. + + For example, localize your user messages for the de-DE locale and format them in + a hash table. Save the hash table in a .psd1 file. Then create a de-DE + subdirectory under the script directory, and save the de-DE .psd1 + file in the de-DE subdirectory. Repeat this method for each locale that you support. + + Import-LocalizedData performs a structured search for the localized user + messages for a script. + + Import-LocalizedData begins the search in the directory where the script file + is located (or the value of the BaseDirectory parameter). It then searches within + the base directory for a subdirectory with the same name as the value of the + $PsUICulture variable (or the value of the UICulture parameter), such as de-DE or + ar-SA. Then, it searches in that subdirectory for a .psd1 file with the same name + as the script (or the value of the FileName parameter). + + If Import-LocalizedData cannot find a subdirectory with the name of the UI culture, + or the subdirectory does not contain a .psd1 file for the script, it searches for + a .psd1 file for the script in a subdirectory with the name of the language code, + such as de or ar. If it cannot find the subdirectory or .psd1 file, the command + fails, the data is displayed in the default language in the script, and an error + message is displayed explaining that the data could not be imported. To suppress + the message and fail gracefully, use the ErrorAction common parameter with a value + of SilentlyContinue. + + If Import-LocalizedData finds the subdirectory and the .psd1 file, it imports the + hash table of user messages into the value of the BindingVariable parameter in the + command. Then, when you display a message from the hash table in the variable, the + localized message is displayed. + + For more information, see about_Script_Internationalization. + + .EXAMPLE + $script:localizedData = Get-LocalizedData -DefaultUICulture 'en-US' + + This is an example that can be used in DSC resources to import the + localized strings and if the current UI culture localized folder does + not exist the UI culture 'en-US' is returned. +#> +function Get-LocalizedData +{ + [CmdletBinding(DefaultParameterSetName = 'DefaultUICulture')] + param + ( + [Parameter(Position = 0)] + [Alias('Variable')] + [ValidateNotNullOrEmpty()] + [System.String] + $BindingVariable, + + [Parameter(Position = 1, ParameterSetName = 'TargetedUICulture')] + [System.String] + $UICulture, + + [Parameter()] + [System.String] + $BaseDirectory, + + [Parameter()] + [System.String] + $FileName, + + [Parameter()] + [System.String[]] + $SupportedCommand, + + [Parameter(Position = 1, ParameterSetName = 'DefaultUICulture')] + [System.String] + $DefaultUICulture = 'en-US' + ) + + begin + { + <# + Because Proxy Command changes the Invocation origin, we need to be explicit + when handing the pipeline back to original command. + #> + if ($PSBoundParameters.ContainsKey('FileName')) + { + Write-Debug -Message ('Looking for provided file with base name: ''{0}''.' -f $FileName) + } + else + { + if ($myInvocation.ScriptName) + { + $file = [System.IO.FileInfo] $myInvocation.ScriptName + } + else + { + $file = [System.IO.FileInfo] $myInvocation.MyCommand.Module.Path + } + + $FileName = $file.BaseName + + $PSBoundParameters.Add('FileName', $file.Name) + Write-Debug -Message ('Looking for resolved file with base name: ''{0}''.' -f $FileName) + } + + if ($PSBoundParameters.ContainsKey('BaseDirectory')) + { + $callingScriptRoot = $BaseDirectory + } + else + { + $callingScriptRoot = $MyInvocation.PSScriptRoot + $PSBoundParameters.Add('BaseDirectory', $callingScriptRoot) + } + + # If we're not looking for a specific UICulture, but looking for current culture, one of its parent, or the default. + if (-not $PSBoundParameters.ContainsKey('UICulture') -and $PSBoundParameters.ContainsKey('DefaultUICulture')) + { + <# + We don't want the resolution to eventually return the ModuleManifest + so we run the same GetFilePath() logic than here: + https://github.com/PowerShell/PowerShell/blob/master/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Import-LocalizedData.cs#L302-L333 + and if we see it will return the wrong thing, set the UICulture to DefaultUI culture, and return the logic to Import-LocalizedData. + + If the LCID is 127 (invariant) then use default UI culture anyway. + If we can't create the CultureInfo object, it's probably because the Globalization-invariant mode is enabled for the DotNet runtime (breaking change in .Net) + See more information in issue https://github.com/dsccommunity/DscResource.Common/issues/11. + https://docs.microsoft.com/en-us/dotnet/core/compatibility/globalization/6.0/culture-creation-invariant-mode + #> + + $currentCulture = Get-UICulture + $evaluateDefaultCulture = $true + + if ($currentCulture.LCID -eq 127) + { + try + { + # Current culture is invariant, let's directly evaluate the DefaultUICulture + $currentCulture = New-Object -TypeName 'System.Globalization.CultureInfo' -ArgumentList @($DefaultUICulture) + # No need to evaluate the DefaultUICulture later, as we'll start with this (in the while loop below) + $evaluateDefaultCulture = $false + } + catch + { + Write-Debug -Message 'The Globalization-Invariant mode is enabled, only the Invariant Culture is allowed.' + # The code will now skip to the InvokeCommand part and execute the Get-LocalizedDataForInvariantCulture + } + + $PSBoundParameters['UICulture'] = $DefaultUICulture + } + + [string] $languageFile = '' + [string[]] $localizedFileNamesToTry = @( + ('{0}.psd1' -f $FileName) + ('{0}.strings.psd1' -f $FileName) + ) + + while (-not [string]::IsNullOrEmpty($currentCulture.Name) -and [String]::IsNullOrEmpty($languageFile)) + { + Write-Debug -Message ('Looking for Localized data file using the current culture ''{0}''.' -f $currentCulture.Name) + foreach ($localizedFileName in $localizedFileNamesToTry) + { + $filePath = [System.IO.Path]::Combine($callingScriptRoot, $CurrentCulture.Name, $localizedFileName) + if (Test-Path -Path $filePath) + { + Write-Debug -Message "Found '$filePath'." + $languageFile = $filePath + # Set the filename to the file we found. + $PSBoundParameters['FileName'] = $localizedFileName + # Exit loop if as we found the first filename. + break + } + else + { + Write-Debug -Message "File '$filePath' not found." + } + } + + if ([String]::IsNullOrEmpty($languageFile)) + { + <# + Evaluate the parent culture if there is a valid one (not Invariant). + + If the parent culture is LCID 127 then move to the default culture. + See more information in issue https://github.com/dsccommunity/DscResource.Common/issues/11. + #> + if ($currentCulture.Parent -and [string]$currentCulture.Parent.Name) + { + $currentCulture = $currentCulture.Parent + } + else + { + if ($evaluateDefaultCulture) + { + $evaluateDefaultCulture = $false + + <# + Could not find localized strings file for the the operating + system UI culture. Evaluating the default UI culture (which + defaults to 'en-US' if not specifically set). + #> + try + { + $currentCulture = New-Object -TypeName 'System.Globalization.CultureInfo' -ArgumentList @($DefaultUICulture) + } + catch + { + Write-Debug -Message ('Unable to create the Default UI Culture [CultureInfo] object, most likely due to invariant mode being enabled.') + $currentCulture = Get-UICulture + # We already tried everything we could, exit the while loop and hand over to Import-LocalizedData or Get-LocalizedDataForInvariantCultureMode + break + } + + $PSBoundParameters['UICulture'] = $DefaultUICulture + } + else + { + <# + Already evaluated everything we could, exit and let + Import-LocalizedData throw an exception. + #> + break + } + } + } + } + + <# + Removes the parameter DefaultUICulture so that isn't used when + calling Import-LocalizedData. + #> + $null = $PSBoundParameters.Remove('DefaultUICulture') + } + + try + { + $outBuffer = $null + + if ($PSBoundParameters.TryGetValue('OutBuffer', [ref] $outBuffer)) + { + $PSBoundParameters['OutBuffer'] = 1 + } + + if ($currentCulture.LCID -eq 127) + { + # Culture is invariant, working around issue with Import-LocalizedData when pwsh configured as invariant + $wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand('Get-LocalizedDataForInvariantCulture', [System.Management.Automation.CommandTypes]::Function) + $PSBoundParameters.Keys.ForEach({ + if ($_ -notin $wrappedCmd.Parameters.Keys) + { + $PSBoundParameters.Remove($_) + } + }) + + $scriptCmd = { & $wrappedCmd @PSBoundParameters } + } + else + { + <# Action when all if and elseif conditions are false #> + $wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand('Microsoft.PowerShell.Utility\Import-LocalizedData', [System.Management.Automation.CommandTypes]::Cmdlet) + $scriptCmd = { & $wrappedCmd @PSBoundParameters } + } + + $steppablePipeline = $scriptCmd.GetSteppablePipeline($myInvocation.CommandOrigin) + $steppablePipeline.Begin($PSCmdlet) + } + catch + { + throw + } + } + + process + { + try + { + $steppablePipeline.Process($_) + } + catch + { + throw + } + } + + end + { + if ($BindingVariable -and ($valueToBind = Get-Variable -Name $BindingVariable -ValueOnly -ErrorAction 'Ignore')) + { + # Bringing the variable to the parent scope + Set-Variable -Scope 1 -Name $BindingVariable -Force -ErrorAction 'SilentlyContinue' -Value $valueToBind + } + + try + { + $steppablePipeline.End() + } + catch + { + throw + } + } +} +#EndRegion './Public/Get-LocalizedData.ps1' 397 +#Region './Public/Get-LocalizedDataForInvariantCulture.ps1' 0 + +<# + .SYNOPSIS + Gets language-specific data when the culture is invariant. + This directly gets the data from the DefaultUICulture, but without calling + "Import-LocalizedData" which throws when the pwsh session is configured to be + of invariant culture (as in the Guest Config agent). + + .DESCRIPTION + The Get-LocalizedDataForInvariantCulture grabs the data from a localized string data psd1 file, + without calling Import-LocalizedData which errors when called in a powershell session with the + Globalization-Invariant mode enabled + (https://docs.microsoft.com/en-us/dotnet/core/compatibility/globalization/6.0/culture-creation-invariant-mode). + + Instead, this function reads and executes the content of a psd1 file in a + constrained language mode that only allows basic ConvertFrom-stringData. + + .PARAMETER BaseDirectory + Specifies the base directory where the .psd1 files are located. The default is + the directory where the script is located. Import-LocalizedData searches for + the .psd1 file for the script in a language-specific subdirectory of the base + directory. + + .PARAMETER FileName + Specifies the base name of the data file (.psd1) to be imported. Enter a file name. + You can specify a file name that does not include its .psd1 file name extension, + or you can specify the file name including the .psd1 file name extension. + + The FileName parameter is required when Get-LocalizedDataForInvariantCulture is not used in a + script. Otherwise, the parameter is optional and the default value is the base + name of the calling script. You can use this parameter to directly search for a + specific .psd1 file. + + For example, if the FileName is omitted and the script name is FindFiles.ps1, + Get-LocalizedDataForInvariantCulture searches for the FindFiles.psd1 or + FindFiles.strings.psd1 data file. + + .PARAMETER SupportedCommand + Specifies cmdlets and functions that generate only data. + + Use this parameter to include cmdlets and functions that you have written or + tested. For more information, see about_Script_Internationalization. + + .PARAMETER DefaultUICulture + Specifies which UICulture to default to if current UI culture or its parents + culture don't have matching data file. + + For example, if you have a data file in 'en-US' but not in 'en' or 'en-GB' and + your current culture is 'en-GB', you can default back to 'en-US'. + + .NOTES + The Get-LocalizedDataForInvariantCulture should only be used when we want to avoid + using Import-LocalizedData, such as when doing so will fail because the powershell session + is in Globalization-Invariant mode: + https://docs.microsoft.com/en-us/dotnet/core/compatibility/globalization/6.0/culture-creation-invariant-mode + + Before using Get-LocalizedDataForInvariantCulture, localize your user messages to the desired + default locale (UI culture, usually en-US) in a hash table of key/value pairs, and save the + hash table in a file with the same name as the script or module with a .psd1 file name extension. + Create a directory under the module base or script's parent directory for each supported UI culture, + and then save the .psd1 file for each UI culture in the directory with the UI culture name. + + For example, localize your user messages for the de-DE locale and format them in + a hash table. Save the hash table in a .psd1 file. Then create a de-DE + subdirectory under the script directory, and save the de-DE .psd1 + file in the de-DE subdirectory. Repeat this method for each locale that you support. + + Import-LocalizedData performs a structured search for the localized user + messages for a script. + + Get-LocalizedDataForInvariantCulture only search in the BaseDirectory specified. + It then searches within the base directory for a subdirectory with the same name + as the value of the $DefaultUICulture variable (specified or default to en-US), + such as de-DE or ar-SA. + Then, it searches in that subdirectory for a .psd1 file with the same name + as provided FileName such as FileName.psd1 or FileName.strings.psd1. + + .EXAMPLE + Get-LocalizedDataForInvariantCulture -BaseDirectory .\source\ -FileName DscResource.Common -DefaultUICulture en-US + + This is an example, usually it is only used by Get-LocalizedData in DSC resources to import the + localized strings when the Culture is Invariant (id 127). +#> +function Get-LocalizedDataForInvariantCulture +{ + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [System.String] + $BaseDirectory, + + [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] + [System.String] + $FileName, + + [Parameter()] + [System.String] + [ValidateNotNull()] + $DefaultUICulture = 'en-US' + ) + + begin + { + if ($FileName -match '\.psm1$|\.ps1$|\.psd1$') + { + Write-Debug -Message 'Found an extension to the file name to search. Stripping...' + $FileName = $FileName -replace '\.psm1$|\.ps1$|\.psd1$' + } + + [string] $languageFile = '' + $localizedFolder = Join-Path -Path $BaseDirectory -ChildPath $DefaultUICulture + [string[]] $localizedFileNamesToTry = @( + ('{0}.psd1' -f $FileName) + ('{0}.strings.psd1' -f $FileName) + ) + + foreach ($localizedFileName in $localizedFileNamesToTry) + { + $filePath = [System.IO.Path]::Combine($localizedFolder, $localizedFileName) + if (Test-Path -Path $filePath) + { + Write-Debug -Message "Found '$filePath'." + $languageFile = $filePath + # Exit loop as we found the first filename. + break + } + else + { + Write-Debug -Message "File '$filePath' not found." + } + } + + if ([string]::IsNullOrEmpty($languageFile)) + { + throw ('File ''{0}'' not found in ''{1}''.' -f ($localizedFileNamesToTry -join ','),$localizedFolder) + } + else + { + Write-Verbose -Message ('Getting file {0}' -f $languageFile) + } + + $constrainedState = [System.Management.Automation.Runspaces.InitialSessionState]::Create() + + if (!$IsCoreCLR) + { + $constrainedState.ApartmentState = [System.Threading.ApartmentState]::STA + } + + $constrainedState.LanguageMode = [System.Management.Automation.PSLanguageMode]::ConstrainedLanguage + $constrainedState.DisableFormatUpdates = $true + + $sspe = New-Object System.Management.Automation.Runspaces.SessionStateProviderEntry 'Environment',([Microsoft.PowerShell.Commands.EnvironmentProvider]),$null + $constrainedState.Providers.Add($sspe) + + $sspe = New-Object System.Management.Automation.Runspaces.SessionStateProviderEntry 'FileSystem',([Microsoft.PowerShell.Commands.FileSystemProvider]),$null + $constrainedState.Providers.Add($sspe) + + $ssce = New-Object System.Management.Automation.Runspaces.SessionStateCmdletEntry 'Get-Content',([Microsoft.PowerShell.Commands.GetContentCommand]),$null + $constrainedState.Commands.Add($ssce) + + $ssce = New-Object System.Management.Automation.Runspaces.SessionStateCmdletEntry 'Get-Date',([Microsoft.PowerShell.Commands.GetDateCommand]),$null + $constrainedState.Commands.Add($ssce) + + $ssce = New-Object System.Management.Automation.Runspaces.SessionStateCmdletEntry 'Get-ChildItem',([Microsoft.PowerShell.Commands.GetChildItemCommand]),$null + $constrainedState.Commands.Add($ssce) + + $ssce = New-Object System.Management.Automation.Runspaces.SessionStateCmdletEntry 'Get-Item',([Microsoft.PowerShell.Commands.GetItemCommand]),$null + $constrainedState.Commands.Add($ssce) + + $ssce = New-Object System.Management.Automation.Runspaces.SessionStateCmdletEntry 'Test-Path',([Microsoft.PowerShell.Commands.TestPathCommand]),$null + $constrainedState.Commands.Add($ssce) + + $ssce = New-Object System.Management.Automation.Runspaces.SessionStateCmdletEntry 'Out-String',([Microsoft.PowerShell.Commands.OutStringCommand]),$null + $constrainedState.Commands.Add($ssce) + + $ssce = New-Object System.Management.Automation.Runspaces.SessionStateCmdletEntry 'ConvertFrom-StringData',([Microsoft.PowerShell.Commands.ConvertFromStringDataCommand]),$null + $constrainedState.Commands.Add($ssce) + + # $scopedItemOptions = [System.Management.Automation.ScopedItemOptions]::AllScope + + # Create new runspace with the above defined entries. Then open and set its working dir to $destinationAbsolutePath + # so all condition attribute expressions can use a relative path to refer to file paths e.g. + # condition="Test-Path src\${PLASTER_PARAM_ModuleName}.psm1" + $constrainedRunspace = [System.Management.Automation.Runspaces.RunspaceFactory]::CreateRunspace($constrainedState) + $constrainedRunspace.Open() + $destinationAbsolutePath = (Get-Item -Path $BaseDirectory -ErrorAction Stop).FullName + $null = $constrainedRunspace.SessionStateProxy.Path.SetLocation($destinationAbsolutePath) + } + + process + { + try + { + $powershell = [PowerShell]::Create() + $powershell.Runspace = $constrainedRunspace + $expression = Get-Content -Raw -Path $languageFile + try + { + $null = $powershell.AddScript($expression) + $powershell.Invoke() + } + catch + { + throw $_ + } + + # Check for non-terminating errors. + if ($powershell.Streams.Error.Count -gt 0) + { + $powershell.Streams.Error.ForEach({ + Write-Error $_ + }) + } + } + finally + { + if ($powershell) + { + $powershell.Dispose() + } + } + } + + end + { + $constrainedRunspace.Dispose() + } +} +#EndRegion './Public/Get-LocalizedDataForInvariantCulture.ps1' 230 +#Region './Public/Get-PSModulePath.ps1' 0 + +<# + .SYNOPSIS + Returns the environment variable PSModulePath from the specified target. + + .DESCRIPTION + Returns the environment variable PSModulePath from the specified target. + If more than one target is provided the return will contain all the + concatenation of all unique paths from the targets. + + .PARAMETER FromTarget + Specifies the target to get the PSModulePath from. + + .EXAMPLE + Get-PSModulePath -FromTarget 'Session' + + Returns the paths from the Session target. + + .EXAMPLE + Get-PSModulePath -FromTarget 'Session', 'User', 'Machine' + + Returns the unique paths from the all targets. + + .OUTPUTS + [System.String] + + If there are no paths to return the command will return an empty string. +#> +function Get-PSModulePath +{ + [CmdletBinding()] + [OutputType([System.String])] + param + ( + [Parameter(Mandatory = $true)] + [ValidateSet('Session', 'User', 'Machine')] + [System.String[]] + $FromTarget + ) + + $modulePathSession = $modulePathUser = $modulePathMachine = $null + + <# + Get the environment variables from required targets. The value returned + is cast to System.String to convert $null values to empty string. + #> + switch ($FromTarget) + { + 'Session' + { + $modulePathSession = Get-EnvironmentVariable -Name 'PSModulePath' + } + + 'User' + { + $modulePathUser = Get-EnvironmentVariable -Name 'PSModulePath' -FromTarget 'User' + } + + 'Machine' + { + $modulePathMachine = Get-EnvironmentVariable -Name 'PSModulePath' -FromTarget 'Machine' + } + } + + $modulePath = $modulePathSession, $modulePathUser, $modulePathMachine -join ';' + + $modulePathArray = $modulePath -split ';' | + Where-Object -FilterScript { + -not [System.String]::IsNullOrEmpty($_) + } | + Sort-Object -Unique + + $modulePath = $modulePathArray -join ';' + + return $modulePath +} +#EndRegion './Public/Get-PSModulePath.ps1' 77 +#Region './Public/Get-TemporaryFolder.ps1' 0 +<# + .SYNOPSIS + Returns the path of the current user's temporary folder. + + .DESCRIPTION + Returns the path of the current user's temporary folder. + + .NOTES + This is the same as doing the following + - Windows: $env:TEMP + - macOS: $env:TMPDIR + - Linux: /tmp/ + + .EXAMPLE + Get-TemporaryFolder + + Returns the current user temporary folder on the current operating system. +#> +function Get-TemporaryFolder +{ + [CmdletBinding()] + [OutputType([System.String])] + param () + + return [IO.Path]::GetTempPath() +} +#EndRegion './Public/Get-TemporaryFolder.ps1' 27 +#Region './Public/New-InvalidArgumentException.ps1' 0 +<# + .SYNOPSIS + Creates and throws an invalid argument exception. + + .DESCRIPTION + Creates and throws an invalid argument exception. + + .PARAMETER Message + The message explaining why this error is being thrown. + + .PARAMETER ArgumentName + The name of the invalid argument that is causing this error to be thrown. + + .EXAMPLE + $errorMessage = $script:localizedData.ActionCannotBeUsedInThisContextMessage ` + -f $Action, $Parameter + + New-InvalidArgumentException -ArgumentName 'Action' -Message $errorMessage +#> +function New-InvalidArgumentException +{ + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '')] + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.String] + $Message, + + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.String] + $ArgumentName + ) + + $argumentException = New-Object -TypeName 'ArgumentException' ` + -ArgumentList @($Message, $ArgumentName) + + $newObjectParameters = @{ + TypeName = 'System.Management.Automation.ErrorRecord' + ArgumentList = @($argumentException, $ArgumentName, 'InvalidArgument', $null) + } + + $errorRecord = New-Object @newObjectParameters + + throw $errorRecord +} +#EndRegion './Public/New-InvalidArgumentException.ps1' 49 +#Region './Public/New-InvalidDataException.ps1' 0 +<# + .SYNOPSIS + Creates and throws an invalid data exception. + + .DESCRIPTION + Creates and throws an invalid data exception. + + .PARAMETER ErrorId + The error Id to assign to the exception. + + .PARAMETER ErrorMessage + The error message to assign to the exception. + + .EXAMPLE + if ( -not $resultOfEvaluation ) + { + $errorMessage = $script:localizedData.InvalidData -f $Action + + New-InvalidDataException -ErrorId 'InvalidDataError' -ErrorMessage $errorMessage + } +#> +function New-InvalidDataException +{ + [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '')] + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [System.String] + $ErrorId, + + [Parameter(Mandatory = $true)] + [System.String] + $ErrorMessage + ) + + $errorCategory = [System.Management.Automation.ErrorCategory]::InvalidData + $exception = New-Object ` + -TypeName System.InvalidOperationException ` + -ArgumentList $ErrorMessage + $errorRecord = New-Object ` + -TypeName System.Management.Automation.ErrorRecord ` + -ArgumentList $exception, $ErrorId, $errorCategory, $null + + throw $errorRecord +} +#EndRegion './Public/New-InvalidDataException.ps1' 47 +#Region './Public/New-InvalidOperationException.ps1' 0 +<# + .SYNOPSIS + Creates and throws an invalid operation exception. + + .DESCRIPTION + Creates and throws an invalid operation exception. + + .PARAMETER Message + The message explaining why this error is being thrown. + + .PARAMETER ErrorRecord + The error record containing the exception that is causing this terminating error. + + .EXAMPLE + try + { + Start-Process @startProcessArguments + } + catch + { + $errorMessage = $script:localizedData.InstallationFailedMessage -f $Path, $processId + New-InvalidOperationException -Message $errorMessage -ErrorRecord $_ + } +#> +function New-InvalidOperationException +{ + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '')] + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.String] + $Message, + + [Parameter()] + [ValidateNotNull()] + [System.Management.Automation.ErrorRecord] + $ErrorRecord + ) + + if ($null -eq $ErrorRecord) + { + $invalidOperationException = New-Object -TypeName 'InvalidOperationException' ` + -ArgumentList @($Message) + } + else + { + $invalidOperationException = New-Object -TypeName 'InvalidOperationException' ` + -ArgumentList @($Message, $ErrorRecord.Exception) + } + + $newObjectParameters = @{ + TypeName = 'System.Management.Automation.ErrorRecord' + ArgumentList = @( + $invalidOperationException.ToString(), + 'MachineStateIncorrect', + 'InvalidOperation', + $null + ) + } + + $errorRecordToThrow = New-Object @newObjectParameters + + throw $errorRecordToThrow +} +#EndRegion './Public/New-InvalidOperationException.ps1' 67 +#Region './Public/New-InvalidResultException.ps1' 0 +<# + .SYNOPSIS + Creates and throws an invalid result exception. + + .DESCRIPTION + Creates and throws an invalid result exception. + + .PARAMETER Message + The message explaining why this error is being thrown. + + .PARAMETER ErrorRecord + The error record containing the exception that is causing this terminating error. + + .EXAMPLE + try + { + $numberOfObjects = Get-ChildItem -Path $path + if ($numberOfObjects -eq 0) + { + throw 'To few files.' + } + } + catch + { + $errorMessage = $script:localizedData.TooFewFilesMessage -f $path + New-InvalidResultException -Message $errorMessage -ErrorRecord $_ + } +#> +function New-InvalidResultException +{ + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '')] + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.String] + $Message, + + [Parameter()] + [ValidateNotNull()] + [System.Management.Automation.ErrorRecord] + $ErrorRecord + ) + + if ($null -eq $ErrorRecord) + { + $exception = New-Object -TypeName 'System.Exception' ` + -ArgumentList @($Message) + } + else + { + $exception = New-Object -TypeName 'System.Exception' ` + -ArgumentList @($Message, $ErrorRecord.Exception) + } + + $newObjectParameters = @{ + TypeName = 'System.Management.Automation.ErrorRecord' + ArgumentList = @( + $exception.ToString(), + 'MachineStateIncorrect', + 'InvalidResult', + $null + ) + } + + $errorRecordToThrow = New-Object @newObjectParameters + + throw $errorRecordToThrow +} +#EndRegion './Public/New-InvalidResultException.ps1' 71 +#Region './Public/New-NotImplementedException.ps1' 0 +<# + .SYNOPSIS + Creates and throws an not implemented exception. + + .DESCRIPTION + Creates and throws an not implemented exception. + + .PARAMETER Message + The message explaining why this error is being thrown. + + .PARAMETER ErrorRecord + The error record containing the exception that is causing this terminating error. + + .EXAMPLE + if ($runFeature) + { + $errorMessage = $script:localizedData.FeatureMissing -f $path + New-NotImplementedException -Message $errorMessage -ErrorRecord $_ + } + + Throws an not implemented exception if the variable $runFeature contains + a value. +#> +function New-NotImplementedException +{ + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '')] + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.String] + $Message, + + [Parameter()] + [ValidateNotNull()] + [System.Management.Automation.ErrorRecord] + $ErrorRecord + ) + + if ($null -eq $ErrorRecord) + { + $invalidOperationException = New-Object -TypeName 'NotImplementedException' ` + -ArgumentList @($Message) + } + else + { + $invalidOperationException = New-Object -TypeName 'NotImplementedException' ` + -ArgumentList @($Message, $ErrorRecord.Exception) + } + + $newObjectParameters = @{ + TypeName = 'System.Management.Automation.ErrorRecord' + ArgumentList = @( + $invalidOperationException.ToString(), + 'MachineStateIncorrect', + 'NotImplemented', + $null + ) + } + + $errorRecordToThrow = New-Object @newObjectParameters + + throw $errorRecordToThrow +} +#EndRegion './Public/New-NotImplementedException.ps1' 66 +#Region './Public/New-ObjectNotFoundException.ps1' 0 + +<# + .SYNOPSIS + Creates and throws an object not found exception. + + .DESCRIPTION + Creates and throws an object not found exception. + + .PARAMETER Message + The message explaining why this error is being thrown. + + .PARAMETER ErrorRecord + The error record containing the exception that is causing this terminating error. + + .EXAMPLE + try + { + Get-ChildItem -Path $path + } + catch + { + $errorMessage = $script:localizedData.PathNotFoundMessage -f $path + New-ObjectNotFoundException -Message $errorMessage -ErrorRecord $_ + } +#> +function New-ObjectNotFoundException +{ + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '')] + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.String] + $Message, + + [Parameter()] + [ValidateNotNull()] + [System.Management.Automation.ErrorRecord] + $ErrorRecord + ) + + if ($null -eq $ErrorRecord) + { + $exception = New-Object -TypeName 'System.Exception' ` + -ArgumentList @($Message) + } + else + { + $exception = New-Object -TypeName 'System.Exception' ` + -ArgumentList @($Message, $ErrorRecord.Exception) + } + + $newObjectParameters = @{ + TypeName = 'System.Management.Automation.ErrorRecord' + ArgumentList = @( + $exception.ToString(), + 'MachineStateIncorrect', + 'ObjectNotFound', + $null + ) + } + + $errorRecordToThrow = New-Object @newObjectParameters + + throw $errorRecordToThrow +} +#EndRegion './Public/New-ObjectNotFoundException.ps1' 68 +#Region './Public/Remove-CommonParameter.ps1' 0 +<# + .SYNOPSIS + Removes common parameters from a hashtable. + + .DESCRIPTION + This function serves the purpose of removing common parameters and option + common parameters from a parameter hashtable. + + .PARAMETER Hashtable + The parameter hashtable that should be pruned. + + .EXAMPLE + Remove-CommonParameter -Hashtable $PSBoundParameters + + Returns a new hashtable without the common and optional common parameters. +#> +function Remove-CommonParameter +{ + [Diagnostics.CodeAnalysis.SuppressMessageAttribute( + 'PSUseShouldProcessForStateChangingFunctions', + '', + Justification = 'ShouldProcess is not supported in DSC resources.' + )] + [OutputType([System.Collections.Hashtable])] + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [System.Collections.Hashtable] + $Hashtable + ) + + $inputClone = $Hashtable.Clone() + + $commonParameters = [System.Management.Automation.PSCmdlet]::CommonParameters + $commonParameters += [System.Management.Automation.PSCmdlet]::OptionalCommonParameters + + $Hashtable.Keys | Where-Object -FilterScript { + $_ -in $commonParameters + } | ForEach-Object -Process { + $inputClone.Remove($_) + } + + return $inputClone +} +#EndRegion './Public/Remove-CommonParameter.ps1' 46 +#Region './Public/Set-DscMachineRebootRequired.ps1' 0 +<# + .SYNOPSIS + Set the DSC reboot required status variable. + + .DESCRIPTION + Sets the global DSCMachineStatus variable to a value of 1. + This function is used to set the global variable that indicates + to the LCM that a reboot of the node is required. + + .EXAMPLE + PS C:\> Set-DscMachineRebootRequired + + Sets the $global:DSCMachineStatus variable to 1. + + .NOTES + This function is implemented so that individual resource modules + do not need to use and therefore suppress Global variables + directly. It also enables mocking to increase testability of + consumers. +#> +function Set-DscMachineRebootRequired +{ + [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '')] + # Suppressing this rule because $global:DSCMachineStatus is used to trigger a reboot. + [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidGlobalVars', '')] + <# + Suppressing this rule because $global:DSCMachineStatus is only set, + never used (by design of Desired State Configuration). + #> + [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '')] + [CmdletBinding()] + param + ( + ) + + $global:DSCMachineStatus = 1 +} +#EndRegion './Public/Set-DscMachineRebootRequired.ps1' 38 +#Region './Public/Set-PSModulePath.ps1' 0 + +<# + .SYNOPSIS + Set environment variable PSModulePath in the current session or machine + wide. + + .DESCRIPTION + This is a wrapper to set environment variable PSModulePath in current + session or machine wide. + + .PARAMETER Path + A string with all the paths separated by semi-colons. + + .PARAMETER Machine + If set the PSModulePath will be changed machine wide. If not set, only + the current session will be changed. + + .EXAMPLE + Set-PSModulePath -Path ';' + + .EXAMPLE + Set-PSModulePath -Path ';' -Machine +#> +function Set-PSModulePath +{ + [Diagnostics.CodeAnalysis.SuppressMessageAttribute( + 'PSUseShouldProcessForStateChangingFunctions', + '', + Justification = 'ShouldProcess is not supported in DSC resources.' + )] + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.String] + $Path, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Machine + ) + + if ($Machine.IsPresent) + { + [System.Environment]::SetEnvironmentVariable('PSModulePath', $Path, [System.EnvironmentVariableTarget]::Machine) + } + else + { + $env:PSModulePath = $Path + } +} +#EndRegion './Public/Set-PSModulePath.ps1' 53 +#Region './Public/Test-AccountRequirePassword.ps1' 0 +<# + .SYNOPSIS + Returns whether the specified account require a password to be provided. + + .DESCRIPTION + Returns whether the specified account require a password to be provided. + If the account is a (global) managed service account, virtual account, or a + built-in account then there is no need to provide a password. + + .PARAMETER Name + Credential name for the account. + + .EXAMPLE + Test-AccountRequirePassword -Name 'DOMAIN\MyMSA$' + + Returns $false as a manged service account does not need a password. + + .EXAMPLE + Test-AccountRequirePassword -Name 'DOMAIN\MySqlUser' + + Returns $true as a user account need a password. + + .EXAMPLE + Test-AccountRequirePassword -Name 'NT SERVICE\MSSQL$PAYROLL' + + Returns $false as a virtual account does not need a password. + + .OUTPUTS + [System.Boolean] +#> +function Test-AccountRequirePassword +{ + [CmdletBinding()] + [OutputType([System.Boolean])] + param + ( + [Parameter(Mandatory = $true)] + [System.String] + $Name + ) + + # Assume local or domain service account. + $requirePassword = $true + + switch -Regex ($Name.ToUpper()) + { + # Built-in account. + '^(?:NT ?AUTHORITY\\)?(SYSTEM|LOCALSERVICE|LOCAL SERVICE|NETWORKSERVICE|NETWORK SERVICE)$' # CSpell: disable-line + { + $requirePassword = $false + + break + } + + # Virtual account. + '^(?:NT SERVICE\\)(.*)$' + { + $requirePassword = $false + + break + } + + # (Global) Managed Service Account. + '\$$' + { + $requirePassword = $false + + break + } + } + + return $requirePassword +} +#EndRegion './Public/Test-AccountRequirePassword.ps1' 74 +#Region './Public/Test-DscParameterState.ps1' 0 +<# + .SYNOPSIS + This method is used to test current and desired values for any DSC resource. + + .DESCRIPTION + This function tests the parameter status of DSC resource parameters against + the current values present on the system. + + .PARAMETER CurrentValues + A hashtable with the current values on the system, obtained by e.g. + Get-TargetResource. + + .PARAMETER DesiredValues + The hashtable of desired values. For example $PSBoundParameters with the + desired values. + + .PARAMETER Properties + This is a list of properties in the desired values list should be checked. + If this is empty then all values in DesiredValues are checked. + + .PARAMETER ExcludeProperties + This is a list of which properties in the desired values list should be checked. + If this is empty then all values in DesiredValues are checked. + + .PARAMETER TurnOffTypeChecking + Indicates that the type of the parameter should not be checked. + + .PARAMETER ReverseCheck + Indicates that a reverse check should be done. The current and desired state + are swapped for another test. + + .PARAMETER SortArrayValues + If the sorting of array values does not matter, values are sorted internally + before doing the comparison. + + .EXAMPLE + $currentState = Get-TargetResource @PSBoundParameters + + $returnValue = Test-DscParameterState -CurrentValues $currentState -DesiredValues $PSBoundParameters + + The function Get-TargetResource is called first using all bound parameters + to get the values in the current state. The result is then compared to the + desired state by calling `Test-DscParameterState`. + + .EXAMPLE + $getTargetResourceParameters = @{ + ServerName = $ServerName + InstanceName = $InstanceName + Name = $Name + } + + $returnValue = Test-DscParameterState ` + -CurrentValues (Get-TargetResource @getTargetResourceParameters) ` + -DesiredValues $PSBoundParameters ` + -ExcludeProperties @( + 'FailsafeOperator' + 'NotificationMethod' + ) + + This compares the values in the current state against the desires state. + The function Get-TargetResource is called using just the required parameters + to get the values in the current state. The parameter 'ExcludeProperties' + is used to exclude the properties 'FailsafeOperator' and + 'NotificationMethod' from the comparison. + + .EXAMPLE + $getTargetResourceParameters = @{ + ServerName = $ServerName + InstanceName = $InstanceName + Name = $Name + } + + $returnValue = Test-DscParameterState ` + -CurrentValues (Get-TargetResource @getTargetResourceParameters) ` + -DesiredValues $PSBoundParameters ` + -Properties ServerName, Name + + This compares the values in the current state against the desires state. + The function Get-TargetResource is called using just the required parameters + to get the values in the current state. The 'Properties' parameter is used + to to only compare the properties 'ServerName' and 'Name'. +#> +function Test-DscParameterState +{ + [CmdletBinding()] + [OutputType([Bool])] + param + ( + [Parameter(Mandatory = $true)] + [System.Object] + $CurrentValues, + + [Parameter(Mandatory = $true)] + [System.Object] + $DesiredValues, + + [Parameter()] + [System.String[]] + [Alias('ValuesToCheck')] + $Properties, + + [Parameter()] + [System.String[]] + $ExcludeProperties, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $TurnOffTypeChecking, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $ReverseCheck, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $SortArrayValues + ) + + $returnValue = $true + + $resultCompare = Compare-DscParameterState @PSBoundParameters + + if ($resultCompare.InDesiredState -contains $false) + { + $returnValue = $false + } + + return $returnValue +} +#EndRegion './Public/Test-DscParameterState.ps1' 130 +#Region './Public/Test-DscProperty.ps1' 0 +<# + .SYNOPSIS + Tests whether the class-based resource has the specified property. + + .DESCRIPTION + Tests whether the class-based resource has the specified property. + + .PARAMETER InputObject + Specifies the object that should be tested for existens of the specified + property. + + .PARAMETER Name + Specifies the name of the property. + + .PARAMETER HasValue + Specifies if the property should be evaluated to have a non-value. If + the property exist but is assigned `$null` the command returns `$false`. + + .PARAMETER Attribute + Specifies if the property should be evaluated to have a specific attribute. + If the property exist but is not the specific attribute the command returns + `$false`. + + .EXAMPLE + Test-DscProperty -InputObject $this -Name 'MyDscProperty' + + Returns $true or $false whether the property exist or not. + + .EXAMPLE + $this | Test-DscProperty -Name 'MyDscProperty' + + Returns $true or $false whether the property exist or not. + + .EXAMPLE + Test-DscProperty -InputObject $this -Name 'MyDscProperty' -HasValue + + Returns $true if the property exist and is assigned a non-null value, if not + $false is returned. + + .EXAMPLE + Test-DscProperty -InputObject $this -Name 'MyDscProperty' -Attribute 'Optional' + + Returns `$true` if the property exist and is an optional property. + + .EXAMPLE + Test-DscProperty -InputObject $this -Name 'MyDscProperty' -Attribute 'Optional' -HasValue + + Returns `$true` if the property exist, is an optional property, and is + assigned a non-null value. + + .OUTPUTS + [System.Boolean] + + .NOTES + This command only works with nullable data types, if using a non-nullable + type make sure to make it nullable, e.g. [Nullable[System.Int32]]. +#> +function Test-DscProperty +{ + [CmdletBinding()] + [OutputType([System.Boolean])] + param + ( + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] + [PSObject] + $InputObject, + + [Parameter(Mandatory = $true)] + [System.String] + $Name, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $HasValue, + + [Parameter()] + [ValidateSet('Key', 'Mandatory', 'NotConfigurable', 'Optional')] + [System.String[]] + $Attribute + ) + + begin + { + $hasProperty = $false + } + + process + { + $isDscProperty = (Get-DscProperty @PSBoundParameters).ContainsKey($Name) + + if ($isDscProperty) + { + $hasProperty = $true + } + } + + end + { + return $hasProperty + } +} +#EndRegion './Public/Test-DscProperty.ps1' 102 +#Region './Public/Test-IsNanoServer.ps1' 0 +<# + .SYNOPSIS + Tests if the current OS is a Nano server. + + .DESCRIPTION + Tests if the current OS is a Nano server. + + .EXAMPLE + Test-IsNanoServer + + Returns $true if the current operating system is Nano Server, if not $false + is returned. +#> +function Test-IsNanoServer +{ + [OutputType([System.Boolean])] + [CmdletBinding()] + param () + + $productDatacenterNanoServer = 143 + $productStandardNanoServer = 144 + + $operatingSystemSKU = (Get-CimInstance -ClassName Win32_OperatingSystem).OperatingSystemSKU + + Write-Verbose -Message ($script:localizedData.TestIsNanoServerOperatingSystemSku -f $operatingSystemSKU) + + return ($operatingSystemSKU -in ($productDatacenterNanoServer, $productStandardNanoServer)) +} +#EndRegion './Public/Test-IsNanoServer.ps1' 29 +#Region './Public/Test-IsNumericType.ps1' 0 +<# + .SYNOPSIS + Returns whether the specified object is of a numeric type. + + .DESCRIPTION + Returns whether the specified object is of a numeric type. + + .PARAMETER Object + The object to test if it is a numeric type. + + .EXAMPLE + Test-IsNumericType -Object ([System.UInt32] 1) + + Returns $true since the object passed is of a numeric type. + + .EXAMPLE + ('a', 2, 'b') | Test-IsNumericType + + Returns $true since one of the values in the array is of a numeric type. + + .OUTPUTS + [System.Boolean] + + .NOTES + When passing in an array of values from the pipeline, the command will return + $true if any of the values in the array is numeric. +#> +function Test-IsNumericType +{ + [CmdletBinding()] + [OutputType([System.Boolean])] + param + ( + [Parameter(ValueFromPipeline = $true)] + [System.Object] + $Object + ) + + begin + { + $isNumeric = $false + } + + process + { + if ( + $Object -is [System.Byte] -or + $Object -is [System.Int16] -or + $Object -is [System.Int32] -or + $Object -is [System.Int64] -or + $Object -is [System.SByte] -or + $Object -is [System.UInt16] -or + $Object -is [System.UInt32] -or + $Object -is [System.UInt64] -or + $Object -is [System.Decimal] -or + $Object -is [System.Double] -or + $Object -is [System.Single] + ) + { + $isNumeric = $true + } + } + + end + { + return $isNumeric + } +} +#EndRegion './Public/Test-IsNumericType.ps1' 69 +#Region './suffix.ps1' 0 +$script:localizedData = Get-LocalizedData -DefaultUICulture 'en-US' +#EndRegion './suffix.ps1' 2 diff --git a/output/RequiredModules/FileSystemDsc/1.1.0/Modules/DscResource.Common/0.16.0/en-US/DscResource.Common.strings.psd1 b/output/RequiredModules/FileSystemDsc/1.1.0/Modules/DscResource.Common/0.16.0/en-US/DscResource.Common.strings.psd1 new file mode 100644 index 00000000..c49bff22 --- /dev/null +++ b/output/RequiredModules/FileSystemDsc/1.1.0/Modules/DscResource.Common/0.16.0/en-US/DscResource.Common.strings.psd1 @@ -0,0 +1,50 @@ +# Localized English (en-US) strings. + +ConvertFrom-StringData @' + TestIsNanoServerOperatingSystemSku = OperatingSystemSKU {0} was returned by Win32_OperatingSystem when detecting if operating system is Nano Server. (DRC0008) + ModuleNotFound = Please ensure that the PowerShell module '{0}' is installed. (DRC0009) + ParameterUsageWrong = None of the parameter(s) '{0}' may be used at the same time as any of the parameter(s) '{1}'. (DRC0010) + AddressFormatError = Address '{0}' is not in the correct format. Please correct the Address parameter in the configuration and try again. (DRC0011) + AddressIPv4MismatchError = Address '{0}' is in IPv4 format, which does not match server address family {1}. Please correct either of them in the configuration and try again. (DRC0012) + AddressIPv6MismatchError = Address '{0}' is in IPv6 format, which does not match server address family {1}. Please correct either of them in the configuration and try again. (DRC0013) + InvalidDesiredValuesError = Property 'DesiredValues' in Test-DscParameterState must be either a Hashtable or CimInstance. Type detected was '{0}'. (DRC0014) + InvalidCurrentValuesError = Property 'CurrentValues' in Test-DscParameterState must be either a Hashtable, CimInstance, or CimIntance[]. Type detected was '{0}'. (DRC0015) + InvalidPropertiesError = If 'DesiredValues' is a CimInstance then property 'Properties' must contain a value. (DRC0016) + MatchPsCredentialUsernameMessage = MATCH: PSCredential username match. Current state is '{0}' and desired state is '{1}'. (DRC0017) + NoMatchPsCredentialUsernameMessage = NOTMATCH: PSCredential username mismatch. Current state is '{0}' and desired state is '{1}'. (DRC0018) + NoMatchTypeMismatchMessage = NOTMATCH: Type mismatch for property '{0}' Current state type is '{1}' and desired type is '{2}'. (DRC0019) + MatchValueMessage = MATCH: Value (type '{0}') for property '{1}' does match. Current state is '{2}' and desired state is '{3}'. (DRC0020) + NoMatchValueMessage = NOTMATCH: Value (type '{0}') for property '{1}' does not match. Current state is '{2}' and desired state is '{3}'. (DRC0021) + NoMatchValueDifferentCountMessage = NOTMATCH: Value (type '{0}') for property '{1}' does have a different count. Current state count is '{2}' and desired state count is '{3}'. (DRC0022) + NoMatchElementTypeMismatchMessage = NOTMATCH: Type mismatch for property '{0}' Current state type of element [{1}] is '{2}' and desired type is '{3}'. (DRC0023) + NoMatchElementValueMismatchMessage = NOTMATCH: Value [{0}] (type '{1}') for property '{2}' does match. Current state is '{3}' and desired state is '{4}'. (DRC0024) + MatchElementValueMessage = MATCH: Value [{0}] (type '{1}') for property '{2}' does match. Current state is '{3}' and desired state is '{4}'. (DRC0025) + PropertyInDesiredStateMessage = Property '{0}' is in desired state. (DRC0026) + StartingReverseCheck = Starting with a reverse check. (DRC0027) + TestDscParameterCompareMessage = Comparing values in property '{0}'. (DRC0028) + TooManyCimInstances = More than one CIM instance was returned from the current state. (DRC0029) + TestingCimInstance = Testing CIM instance '{0}' with the key properties '{1}'. (DRC0030) + MissingCimInstance = The CIM instance '{0}' with the key properties '{1}' is missing. (DRC0031) + ArrayValueIsAbsent = The array value '{0}' is absent. (DRC0032) + ArrayValueIsPresent = The array value '{0}' is present. (DRC0033) + KeyPropertiesMissing = The hashtable passed to function Test-DscPropertyState is missing the key 'KeyProperties'. This must be set to the property names that makes each instance in the CIM instance collection unique. (DRC0034) + ArrayDoesNotMatch = One or more values in an array does not match the desired state. Details of the changes are below. (DRC0035) + PropertyValueOfTypeDoesNotMatch = {0} value does not match. Current value is '{1}', but expected the value '{2}'. (DRC0036) + UnableToCompareType = Unable to compare the type {0} as it is not handled by the Test-DscPropertyState cmdlet. (DRC0037) + EvaluatePropertyState = Evaluating the state of the property '{0}'. (DRC0038) + PropertyInDesiredState = The parameter '{0}' is in desired state. (DRC0039) + PropertyNotInDesiredState = The parameter '{0}' is not in desired state. (DRC0040) + PropertyNotInDesiredStateMessage = Property '{0}' is not in desired state. (DRC0041) + NoMatchKeyMessage = NOTMATCH: Value (type '{0}') for property '{1}' does not match. Current state has the key(s) '{2}' and desired state has not. (DRC0042) + + ## Assert-ElevatedUser + ElevatedUser_UserNotElevated = This command must run in an elevated PowerShell session. (DRC0043) + + ## Assert-RequiredCommandParameter + RequiredCommandParameter_SpecificParametersMustAllBeSet = The parameters '{0}' must all be specified. (DRC0044) + RequiredCommandParameter_SpecificParametersMustAllBeSetWhenParameterExist = The parameters '{0}' must all be specified if either parameter '{1}' is specified. (DRC0045) + + ## Find-Certificate + CertificatePathError = Certificate Path '{0}' is not valid. (DRC0046) + SearchingForCertificateUsingFilters = Looking for certificate in Store '{0}' using filter '{1}'. (DRC0047) +'@ diff --git a/output/RequiredModules/FileSystemDsc/1.1.0/Modules/DscResource.Common/0.16.0/en-US/about_DscResource.Common.help.txt b/output/RequiredModules/FileSystemDsc/1.1.0/Modules/DscResource.Common/0.16.0/en-US/about_DscResource.Common.help.txt new file mode 100644 index 00000000..2a996770 --- /dev/null +++ b/output/RequiredModules/FileSystemDsc/1.1.0/Modules/DscResource.Common/0.16.0/en-US/about_DscResource.Common.help.txt @@ -0,0 +1,26 @@ +TOPIC + about_DscResource.Common + +SHORT DESCRIPTION + Common functions used in DSC tesources. + +LONG DESCRIPTION + This module contains common functions that are used in DSC resources. + +EXAMPLES + PS C:\> Get-Command -Module DscResource.Common + +NOTE: + Thank you to the DSC Community contributors who contributed to this module by + writing code, sharing opinions, and provided feedback. + +TROUBLESHOOTING NOTE: + Go to the Github repository for read about issues, submit a new issue, and read + about new releases. https://github.com/dsccommunity/DscResource.Common + +SEE ALSO + - https://github.com/dsccommunity/DscResource.Common + +KEYWORDS + DSC, Localization + diff --git a/output/RequiredModules/FileSystemDsc/1.1.0/en-US/FileSystemDsc.strings.psd1 b/output/RequiredModules/FileSystemDsc/1.1.0/en-US/FileSystemDsc.strings.psd1 new file mode 100644 index 00000000..b0c45475 --- /dev/null +++ b/output/RequiredModules/FileSystemDsc/1.1.0/en-US/FileSystemDsc.strings.psd1 @@ -0,0 +1,9 @@ +<# + .SYNOPSIS + The localized resource strings in English (en-US) for the + resource FileSystemDsc module. This file should only contain + localized strings for private and public functions. +#> + +ConvertFrom-StringData @' +'@ diff --git a/output/RequiredModules/FileSystemDsc/1.1.0/en-US/about_FileSystemDsc.help.txt b/output/RequiredModules/FileSystemDsc/1.1.0/en-US/about_FileSystemDsc.help.txt new file mode 100644 index 00000000..d6b09779 --- /dev/null +++ b/output/RequiredModules/FileSystemDsc/1.1.0/en-US/about_FileSystemDsc.help.txt @@ -0,0 +1,26 @@ +TOPIC + about_FileSystemDsc + +SHORT DESCRIPTION + This module contains DSC resources for managing file systems. + +LONG DESCRIPTION + This module contains DSC resources for managing file systems. + +EXAMPLES + PS C:\> Get-DscResource -Module FileSystemDsc + +NOTE: + Thank you to the DSC Community contributors who contributed to this module by + writing code, sharing opinions, and provided feedback. + +TROUBLESHOOTING NOTE: + Go to the Github repository for read about issues, submit a new issue, and read + about new releases. https://github.com/dsccommunity/FileSystemDsc + +SEE ALSO + - https://github.com/dsccommunity/SqlServerDsc + +KEYWORDS + DSC, DscResource, FileSystem, File, Directory, Folder, Shares + diff --git a/output/RequiredModules/FileSystemDsc/1.1.0/en-US/about_FileSystemObject.help.txt b/output/RequiredModules/FileSystemDsc/1.1.0/en-US/about_FileSystemObject.help.txt new file mode 100644 index 00000000..16d4500f --- /dev/null +++ b/output/RequiredModules/FileSystemDsc/1.1.0/en-US/about_FileSystemObject.help.txt @@ -0,0 +1,97 @@ +.NAME + FileSystemObject + +.SYNOPSIS + The File resource enables file system operations on Linux and Windows. + With regards to parameters and globbing, it behaves like the Item and Content + cmdlets. + +.DESCRIPTION + + +.PARAMETER DestinationPath + Key - string + The path to create/copy to. + +.PARAMETER SourcePath + Write - string + If data should be copied, the source path to copy from. + +.PARAMETER Ensure + Write - ensure + Indicates if destination should be created or removed. Values: Absent, Present. Default: Present. + +.PARAMETER Type + Write - objectType + The type of the object to create. Values: file, directory, symboliclink. Default: directory + +.PARAMETER Contents + Write - string + The file contents. Unused if type is directory + +.PARAMETER Checksum + Write - checksumType + The type of checksum to use for copy operations. Values: md5, CreationTime, LastModifiedTime. Default: md5 + +.PARAMETER Recurse + Write - bool + Indicates that recurse should be used if data is copied. + +.PARAMETER Force + Write - bool + Indicates that folder structures should be created and existing files overwritten + +.PARAMETER Links + Write - linkBehavior + Link behavior, currently not implemented. Values: follow, manage. Default: follow + +.PARAMETER Group + Write - string + Linux group name for chown, currently not implemented. + +.PARAMETER Mode + Write - string + Linux mode for chmod, currently not implemented. + +.PARAMETER Owner + Write - string + Linux owner name for chown, currently not implemented. + +.PARAMETER CreatedDate + Read - datetime + +.PARAMETER ModifiedDate + Read - datetime + +.PARAMETER Encoding + Write - encoding + File encoding, used with Contents. Values: ASCII, Latin1, UTF7, UTF8, UTF32, BigEndianUnicode, Default, Unicode. Default: Default + +.PARAMETER IgnoreTrailingWhitespace + Write - bool + Indicates that trailing whitespace should be ignored when comparing file contents. + +.PARAMETER Reasons + Read - FileSystemDscReason[] + +.EXAMPLE 1 + + +Sample to create a file with contents. + + +Configuration FileSystemObject_CreateFileWithContent_Config +{ + Import-DscResource -ModuleName FileSystemDsc + + node localhost + { + FileSystemObject MyFile + { + DestinationPath = 'C:\inetpub\wwwroot\index.html' + Contents = 'My PageDSC is the best' + Type = 'file' + Ensure = 'present' + } + } +} diff --git a/source/AllNodes/Dev/DSCFile01.yml b/source/AllNodes/Dev/DSCFile01.yml index e94d3e0b..a6bb22cc 100644 --- a/source/AllNodes/Dev/DSCFile01.yml +++ b/source/AllNodes/Dev/DSCFile01.yml @@ -31,7 +31,7 @@ DscTagging: Layers: - '[x={ Get-DatumSourceFile -Path $File } =]' -FilesAndFolders: +FileSystemObjects: Items: - DestinationPath: Z:\DoesNotWork Type: Directory diff --git a/source/AllNodes/Dev/ReferenceConfigurationDev.yml b/source/AllNodes/Dev/ReferenceConfigurationDev.yml index b1dc6011..1ab1fe22 100644 --- a/source/AllNodes/Dev/ReferenceConfigurationDev.yml +++ b/source/AllNodes/Dev/ReferenceConfigurationDev.yml @@ -31,7 +31,7 @@ DscTagging: Layers: - '[x={ Get-DatumSourceFile -Path $File } =]' -FilesAndFolders: +FileSystemObjects: Items: - DestinationPath: Z:\DoesNotWork Type: Directory diff --git a/source/Baselines/Security.yml b/source/Baselines/Security.yml index 4abb560e..1706501a 100644 --- a/source/Baselines/Security.yml +++ b/source/Baselines/Security.yml @@ -1,6 +1,6 @@ Configurations: - SecurityBase - #- WindowsFeatures + - WindowsFeatures WindowsFeatures: Names: @@ -8,7 +8,7 @@ WindowsFeatures: SecurityBase: Role: Baseline - #DependsOn: '[WindowsFeatures]WindowsFeatures' + DependsOn: '[WindowsFeatures]WindowsFeatures' DscTagging: Layers: diff --git a/source/Baselines/Server.yml b/source/Baselines/Server.yml index b8a30fdc..8bdb9618 100644 --- a/source/Baselines/Server.yml +++ b/source/Baselines/Server.yml @@ -1,6 +1,6 @@ Configurations: - ComputerSettings - - NetworkIpConfiguration + #- NetworkIpConfiguration - WindowsEventLogs NetworkIpConfiguration: diff --git a/source/Datum.yml b/source/Datum.yml index 6019b26f..bd066c8e 100644 --- a/source/Datum.yml +++ b/source/Datum.yml @@ -37,9 +37,9 @@ lookup_options: WindowsFeatures\Names: merge_basetype_array: Unique - FilesAndFolders: + FileSystemObjects: merge_hash: deep - FilesAndFolders\Items: + FileSystemObjects\Items: merge_hash_array: UniqueKeyValTuples merge_options: tuple_keys: diff --git a/source/Environment/Dev.yml b/source/Environment/Dev.yml index e2ec2d2f..f20afb07 100644 --- a/source/Environment/Dev.yml +++ b/source/Environment/Dev.yml @@ -3,7 +3,7 @@ DscTagging: Layers: - '[x={ Get-DatumSourceFile -Path $File } =]' -FilesAndFolders: +FileSystemObjects: Items: - DestinationPath: '[x= "C:\Test\$($File.BaseName)-Environment" =]' Type: Directory diff --git a/source/Environment/Prod.yml b/source/Environment/Prod.yml index 7e645c3b..47545f60 100644 --- a/source/Environment/Prod.yml +++ b/source/Environment/Prod.yml @@ -3,7 +3,7 @@ DscTagging: Layers: - '[x={ Get-DatumSourceFile -Path $File } =]' -FilesAndFolders: +FileSystemObjects: Items: - DestinationPath: '[x= "C:\Test\$($File.BaseName)-Environment" =]' Type: Directory diff --git a/source/Environment/Test.yml b/source/Environment/Test.yml index 7e645c3b..47545f60 100644 --- a/source/Environment/Test.yml +++ b/source/Environment/Test.yml @@ -3,7 +3,7 @@ DscTagging: Layers: - '[x={ Get-DatumSourceFile -Path $File } =]' -FilesAndFolders: +FileSystemObjects: Items: - DestinationPath: '[x= "C:\Test\$($File.BaseName)-Environment" =]' Type: Directory diff --git a/source/Global/Azure.yml b/source/Global/Azure.yml new file mode 100644 index 00000000..00bbd50e --- /dev/null +++ b/source/Global/Azure.yml @@ -0,0 +1,5 @@ +TenantId: 85a26452-f44d-4090-b62b-ae668d70bdeb +SubscriptionId: da78bd51-11ab-418d-8222-db661c2aa8d9 +ResourceGroupName: GCLab1 +StorageAccountName: gclab1sa67 +LocationName: uksouth diff --git a/source/Locations/Frankfurt.yml b/source/Locations/Frankfurt.yml index 4cdca519..8024f50f 100644 --- a/source/Locations/Frankfurt.yml +++ b/source/Locations/Frankfurt.yml @@ -1,7 +1,7 @@ -#Configurations: -# - FilesAndFolders +Configurations: + - FileSystemObjects -FilesAndFolders: +FileSystemObjects: Items: - DestinationPath: '[x= "C:\Test\$($File.BaseName)" =]' Type: Directory diff --git a/source/Locations/London.yml b/source/Locations/London.yml index 4cdca519..8024f50f 100644 --- a/source/Locations/London.yml +++ b/source/Locations/London.yml @@ -1,7 +1,7 @@ -#Configurations: -# - FilesAndFolders +Configurations: + - FileSystemObjects -FilesAndFolders: +FileSystemObjects: Items: - DestinationPath: '[x= "C:\Test\$($File.BaseName)" =]' Type: Directory diff --git a/source/Locations/Singapore.yml b/source/Locations/Singapore.yml index 4cdca519..8024f50f 100644 --- a/source/Locations/Singapore.yml +++ b/source/Locations/Singapore.yml @@ -1,7 +1,7 @@ -#Configurations: -# - FilesAndFolders +Configurations: + - FileSystemObjects -FilesAndFolders: +FileSystemObjects: Items: - DestinationPath: '[x= "C:\Test\$($File.BaseName)" =]' Type: Directory diff --git a/source/Locations/Tokio.yml b/source/Locations/Tokio.yml index 4cdca519..8024f50f 100644 --- a/source/Locations/Tokio.yml +++ b/source/Locations/Tokio.yml @@ -1,7 +1,7 @@ -#Configurations: -# - FilesAndFolders +Configurations: + - FileSystemObjects -FilesAndFolders: +FileSystemObjects: Items: - DestinationPath: '[x= "C:\Test\$($File.BaseName)" =]' Type: Directory diff --git a/source/Roles/FileServer.yml b/source/Roles/FileServer.yml index 1bba1fc8..26511e09 100644 --- a/source/Roles/FileServer.yml +++ b/source/Roles/FileServer.yml @@ -1,25 +1,26 @@ Configurations: - #- FilesAndFolders + - FileSystemObjects - RegistryValues WindowsFeatures: Names: - File-Services -FilesAndFolders: +FileSystemObjects: Items: - DestinationPath: C:\Test Type: Directory - DestinationPath: C:\Test\Test1File1.txt Type: File Contents: Some test data - DependsOn: '[File]file_C__Test' + DependsOn: '[FileSystemObject]FileSystemObject_C__Test' - DestinationPath: C:\Test\Test1File2.txt Type: File Contents: Some test data - DependsOn: '[File]file_C__Test' + DependsOn: '[FileSystemObject]FileSystemObject_C__Test' - DestinationPath: C:\GpoBackup - SourcePath: '[x= "\\DSCDC01\SYSVOL\$($Datum.Global.Domain.DomainFqdn)\Policies" =]' + #SourcePath: '[x= "\\DSCDC01\SYSVOL\$($Datum.Global.Domain.DomainFqdn)\Policies" =]' + SourcePath: C:\Windows\Media Type: Directory RegistryValues: @@ -30,7 +31,7 @@ RegistryValues: ValueType: String Ensure: Present Force: true - #DependsOn: '[FilesAndFolders]FilesAndFolders' + DependsOn: '[FileSystemObjects]FileSystemObjects' SecurityBaseline: Role: FileServer diff --git a/source/Roles/WebServer.yml b/source/Roles/WebServer.yml index 0cb9ad4d..15d606fb 100644 --- a/source/Roles/WebServer.yml +++ b/source/Roles/WebServer.yml @@ -1,11 +1,11 @@ Configurations: - WindowsServices - RegistryValues - #- FilesAndFolders + - FileSystemObjects - WebApplicationPools - WebApplications -FilesAndFolders: +FileSystemObjects: Items: - DestinationPath: C:\Inetpub\TestApp1 Type: Directory @@ -14,11 +14,11 @@ FilesAndFolders: - DestinationPath: C:\Inetpub\TestApp1\default.html Type: File Contents: This is TestApp1 - DependsOn: '[File]file_C__Inetpub_TestApp1' + DependsOn: '[FileSystemObject]FileSystemObject_C__Inetpub_TestApp1' - DestinationPath: C:\Inetpub\TestApp2\default.html Type: File Contents: This is TestApp2 - DependsOn: '[File]file_C__Inetpub_TestApp2' + DependsOn: '[FileSystemObject]FileSystemObject_C__Inetpub_TestApp2' WebApplicationPools: Items: @@ -32,8 +32,8 @@ WebApplicationPools: State: Started DependsOn: '[WebAppPool]TestAppPool1' DependsOn: - #- '[FilesAndFolders]FilesAndFolders' - #- '[WindowsFeatures]WindowsFeatures' + - '[FileSystemObjects]FileSystemObjects' + - '[WindowsFeatures]WindowsFeatures' WebApplications: Items: diff --git a/source/_TestRsopReferences/ReferenceConfigurationDev.yml b/source/_TestRsopReferences/ReferenceConfigurationDev.yml index f506c576..45903b53 100644 --- a/source/_TestRsopReferences/ReferenceConfigurationDev.yml +++ b/source/_TestRsopReferences/ReferenceConfigurationDev.yml @@ -34,7 +34,7 @@ DscLcmMaintenanceWindows: Timespan: 24:00:00 DependsOn: '[DscLcmController]DscLcmController' Configurations: -- FilesAndFolders +- FileSystemObjects - RegistryValues - SecurityBase - WindowsFeatures @@ -68,7 +68,7 @@ DscTagging: - Baselines\Security - Baselines\Server - Baselines\DscLcm -FilesAndFolders: +FileSystemObjects: Items: - DestinationPath: Z:\DoesNotWork Type: Directory @@ -80,7 +80,7 @@ FilesAndFolders: Type: Directory - Contents: Some test data Type: File - DependsOn: '[File]file_C__Test' + DependsOn: '[FileSystemObject]FileSystemObject_C__Test' DestinationPath: C:\Test\Test1File1.txt - Contents: Some test data Type: File @@ -93,7 +93,7 @@ NodeName: ReferenceConfigurationDev Environment: Dev Location: Frankfurt RegistryValues: - DependsOn: '[FilesAndFolders]FilesAndFolders' + DependsOn: '[FileSystemObjects]FileSystemObjects' Values: - ValueName: DBFlag ValueType: DWORD diff --git a/source/_TestRsopReferences/ReferenceConfigurationProd.yml b/source/_TestRsopReferences/ReferenceConfigurationProd.yml index 4cb907c4..cb96b5ce 100644 --- a/source/_TestRsopReferences/ReferenceConfigurationProd.yml +++ b/source/_TestRsopReferences/ReferenceConfigurationProd.yml @@ -34,7 +34,7 @@ DscLcmMaintenanceWindows: Timespan: 24:00:00 DependsOn: '[DscLcmController]DscLcmController' Configurations: -- FilesAndFolders +- FileSystemObjects - RegistryValues - SecurityBase - WindowsFeatures @@ -68,7 +68,7 @@ DscTagging: - Baselines\Security - Baselines\Server - Baselines\DscLcm -FilesAndFolders: +FileSystemObjects: Items: - DestinationPath: C:\Test\Prod-Environment Type: Directory @@ -91,7 +91,7 @@ NodeName: ReferenceConfigurationProd Environment: Prod Location: Frankfurt RegistryValues: - DependsOn: '[FilesAndFolders]FilesAndFolders' + DependsOn: '[FileSystemObjects]FileSystemObjects' Values: ValueName: NtpServer ValueType: String diff --git a/source/_TestRsopReferences/ReferenceConfigurationTest.yml b/source/_TestRsopReferences/ReferenceConfigurationTest.yml index 9d0132fa..4e4321d8 100644 --- a/source/_TestRsopReferences/ReferenceConfigurationTest.yml +++ b/source/_TestRsopReferences/ReferenceConfigurationTest.yml @@ -35,7 +35,7 @@ DscLcmMaintenanceWindows: Timespan: 24:00:00 DependsOn: '[DscLcmController]DscLcmController' Configurations: -- FilesAndFolders +- FileSystemObjects - WindowsServices - RegistryValues - WebApplicationPools @@ -91,9 +91,9 @@ WebApplicationPools: IdentityType: ApplicationPoolIdentity State: Started DependsOn: - - '[FilesAndFolders]FilesAndFolders' + - '[FileSystemObjects]FileSystemObjects' - '[WindowsFeatures]WindowsFeatures' -FilesAndFolders: +FileSystemObjects: Items: - DestinationPath: C:\Test\Test-Environment Type: Directory