Skip to content

Instantly share code, notes, and snippets.

@FlorianAlikoff
Last active August 29, 2015 13:57
Show Gist options
  • Save FlorianAlikoff/8a22e4c3fd9d36417deb to your computer and use it in GitHub Desktop.
Save FlorianAlikoff/8a22e4c3fd9d36417deb to your computer and use it in GitHub Desktop.
##########################################################
# #
# Script PowerShell v4.0 #
# Pull Server Configuration #
# #
# Name : Pull Server Configuration.ps1 #
# Author : Florian Alikoff #
# Date : 10/02/2014 #
# #
##########################################################
##################################################################
# Functions #
##################################################################
Configuration DSCPullServer {
<#
.SYNOPSIS
Generate pull configuration for LocalConfigurationManager ressource for specific computers.
.DESCRIPTION
Generate pull configuration for LocalConfigurationManager ressource for specific computers.
.PARAMETER Computer
Pull server name
.PARAMETER ShareName
Pull server share
.PARAMETER FolderName
Pull server share path
.PARAMETER FilterMode
Pull clients filter name (name of an organizational unit ou AD group)
.PARAMETER ClientFilter
Pull clients filter (organizational unit or AD group)
.PARAMETER SourceScriptPath
Folder path containing script (Library Pull Configuration.ps1) that will be retrieve by DSC file ressource on pull server.
.PARAMETER ConfigurationFolder
Folder containing configuration file (MOF) on pull share (containing MOF file with GUID in name and checksum files).
Theses files are auto generated by configuration
.PARAMETER OriginConfigurationFolder
Folder containing original configuration file (MOF) on pull share that user can modify.
.PARAMETER ScriptFolder
Folder containing functions on pull share (Library Pull Configuration.ps1). The file will be retrieve and use by the DSC configuration.
.PARAMETER ConfigurationModeFrequencyMins
Frequency between two configuration check in minutes
.PARAMETER RefreshFrequencyMins
Frequency between two refresh of configuration file in minutes (check on pull server)
.EXAMPLE
DSCPullServer -Computer "Server01" -FolderName "C:\PullDSC" -ShareName "PullDSC" -ClientFilter "Pull Clients" `
-FilterMode "Group" -SourceScriptPath "C:\PullServer\Scripts" -ConfigurationFolder "Configuration" `
-ConfigurationModeFrequencyMins 45 -RefreshFrequencyMins 30 `
-OriginConfigurationFolder "OriginConfiguration" -ScriptFolder "Scripts" -OutputPath C:\PSDSC
Description
-----------
It creates a pull server on Server01. A share will be created on C:\PullDSC with name PullDSC (full path \\Server01\PullDSC). Computers
in Active Directory Group will become a client of this Pull server (it generates a LocalConfigurationManager configuration file for each
computer). You only have to Launch Set-LocalConfigurationManager on this computers. LocalconfigurationManager will be configured with
RefreshFrequencyMins to 30 minutes and ConfigurationModeFrequencyMins to 45 minutes. On pull share a folder named Configuration contains
MOF files clients computer retrieve (named with a guid) and a checkum file for each MOF file to verify their integrity. Theses files are
generated automatically by retrieving MOF files created by User on OriginConfiguration folder. Finally there is a Scripts folder containing
powershell script (Library Pull Configuration.ps1) present in C:\PullServer\Scripts. This script (mandatory) is a library to generated
automatically configuration for clients Computer.
#>
param(
[Parameter(Mandatory=$true)]
[String[]] $Computer,
[Parameter(Mandatory=$true)]
[String] $ShareName,
[Parameter(Mandatory=$true)]
[String] $FolderName,
[Parameter(Mandatory=$true)]
[String] $ClientFilter,
[Parameter(Mandatory=$true)]
[String] $FilterMode,
[Parameter(Mandatory=$true)]
[String] $SourceScriptPath,
[Parameter(Mandatory=$true)]
[String] $ConfigurationFolder,
[Parameter(Mandatory=$true)]
[String] $OriginConfigurationFolder,
[Parameter(Mandatory=$true)]
[String] $ScriptFolder,
[Parameter(Mandatory=$true)]
[int] $ConfigurationModeFrequencyMins,
[Parameter(Mandatory=$true)]
[int] $RefreshFrequencyMins
)
#Define path of configuration file generated by administrator
$OriginConfigurationPath = "\\$PullServer\$PullShare\$OriginConfigurationFolder\"
<#Define path of configuration file auto generated (by configuration on pull server).
This files is with a guid name and there is a checksum file for each configuration file.#>
$ConfigurationPath = "\\$PullServer\$PullShare\$ConfigurationFolder\"
#Define
$DestinationScriptPath = "\\$PullServer\$PullShare\$ScriptFolder\"
#Pull server configuration will be generated for a computer
Node $Computer {
<#Script ressource to create folder for the share that contains all configurations files (ComputerName.mof, GUID.mof,
GUID.mof.checksum and ComputerName.meta.mof) and library's scripts #>
Script CreateFolderShare {
#Set folder share
SetScript = $([string]{
#Create folder
New-Item $FolderName -ItemType Directory
}).Replace('$FolderName',"'$FolderName'")
#Test folder existence (return boolean, dsc script ressource pre requisites)
TestScript = $([string]{
try{
Get-Item $FolderName -ErrorAction Stop
return $true
}catch{
return $false
}
}).Replace('$FolderName',"'$FolderName'")
#Retrieve script configuration. Return hashtable (dsc script ressource pre requisites).
GetScript = $([string]{
$Result = ""
try{
$Test = Get-Item $FolderName -ErrorAction Stop
$Result = "$FolderName for Pull Server exist."
}catch{
$Result = "$FolderName for Pull Server doesn't exist."
}
return @{
GetScript = $GetScript
SetScript = $SetScript
TestScript = $TestScript
Result = $Result
}}).Replace('$FolderName',"'$FolderName'")
}
<#Script ressource to add NTFS permissions on Pull SMB folder. Everyone must have read and execute right and Pull server computer
account must have full control right.#>
Script AddNTFSPermission {
#Depends on CreateFolderShare ressource
DependsOn = "[Script]CreateFolderShare"
#Add read and execute permission for everyone group and full control for the pull server computer account
SetScript = $([string]{
#Retrieve everyone group SID
$Sid = New-Object System.Security.Principal.SecurityIdentifier([System.Security.Principal.WellKnownSidType]::WorldSid, $null)
#Get ACL on Pull server SMB folder
$ACL = Get-Acl -Path $FolderName
#Create permission for everyone group
$Permission = $Sid,"ReadAndExecute","ContainerInherit,ObjectInherit","None","Allow"
$AccessRule = New-Object System.Security.AccessControl.FileSystemAccessRule $Permission
#Add ACL on ACL list
$ACL.SetAccessRule($AccessRule)
#Get computer account
$User = New-Object System.Security.Principal.NTAccount(($env:userdomain+"\"+$PullServer+"$"))
$StrSID = $User.Translate([System.Security.Principal.SecurityIdentifier])
#Retrieve Sid
$Sid = New-Object security.principal.securityidentifier($StrSID)
#Create permission for computer account
$Permission = $Sid,"FullControl","ContainerInherit,ObjectInherit","None","Allow"
$AccessRule = New-Object System.Security.AccessControl.FileSystemAccessRule $Permission
#Add ACL on ACL list
$ACL.SetAccessRule($AccessRule)
#Set ACL list on folder
$ACL | Set-Acl $FolderName
}).Replace('$FolderName',"'$FolderName'").Replace('$PullServer',"'$PullServer'")
#Test existence of read and execute permission for everyone group and full control for the pull server computer account
TestScript = $([string]{
#If error when retrieving permissions, result is false
try{
#Get ACL for everyone group
$ACLEveryone = (Get-Acl -Path $FolderName -ErrorAction Stop).Access | `
Where-Object {($_.FileSystemRights -like '*ReadAndExecute*') `
-and ($_.AccessControlType -eq 'Allow') -and `
($_.IdentityReference.Translate([System.Security.Principal.SecurityIdentifier]) -eq `
(New-Object System.Security.Principal.SecurityIdentifier([System.Security.Principal.WellKnownSidType]::WorldSid, $null)))}
#Get user
$User = New-Object System.Security.Principal.NTAccount(($env:userdomain+"\"+$PullServer+"$"))
$StrSID = $User.Translate([System.Security.Principal.SecurityIdentifier])
#Retrieve Sid
$Sid = New-Object security.principal.securityidentifier($StrSID)
#Get ACL for computer account
$ACLComputer = (Get-Acl -Path $FolderName -ErrorAction Stop).Access | `
Where-Object {($_.FileSystemRights -like '*FullControl*') `
-and ($_.AccessControlType -eq 'Allow') -and `
($_.IdentityReference.Translate([System.Security.Principal.SecurityIdentifier]) -eq $Sid)}
}catch{
return $false
}
#If permission has been found result is true, otherwise result is false
if(($ACLEveryone.count -gt 0) -and ($ACLComputer.count -gt 0)){
return $true
}else{
return $false
}
}).Replace('$FolderName',"'$FolderName'").Replace('$PullServer',"'$PullServer'")
#Get script configuration and retrieve result about NTFS permission on pull server SMB folder (return hashtable).
GetScript = $([string]{
$Result = ""
try{
#Get ACL for everyone group
$ACLEveryone = (Get-Acl -Path $FolderName -ErrorAction Stop).Access | `
Where-Object {($_.FileSystemRights -like '*ReadAndExecute*') `
-and ($_.AccessControlType -eq 'Allow') -and `
($_.IdentityReference.Translate([System.Security.Principal.SecurityIdentifier]) -eq `
(New-Object System.Security.Principal.SecurityIdentifier([System.Security.Principal.WellKnownSidType]::WorldSid, $null)))}
#Get user
$User = New-Object System.Security.Principal.NTAccount(($env:userdomain+"\"+$PullServer+"$"))
$StrSID = $User.Translate([System.Security.Principal.SecurityIdentifier])
#Retrieve Sid
$Sid = New-Object security.principal.securityidentifier($StrSID)
#Get ACL for computer account
$ACLComputer = (Get-Acl -Path $FolderName -ErrorAction Stop).Access | `
Where-Object {($_.FileSystemRights -like '*FullControl*') `
-and ($_.AccessControlType -eq 'Allow') -and `
($_.IdentityReference.Translate([System.Security.Principal.SecurityIdentifier]) -eq $Sid)}
}catch{
$Result = "Error on retrieving ACL. "
}
#Result is successfull if permission has been found for everyone group
if($ACLEveryone.count -gt 0){
$Result = "Everyone ReadAndExecute ACL has been found on $FolderName. "
}else{
$Result = "No Everyone ReadAndExecute ACL has been retrieve on $FolderName. "
}
#Result is successfull if permission has been found for computer account
if($ACLComputer.count -gt 0){
$Result += "$PullServer`$ FullControl ACL has been found on $FolderName. "
}else{
$Result += "No $PullServer`$ FullControl ACL has been retrieve on $FolderName. "
}
return @{
GetScript = $GetScript
SetScript = $SetScript
TestScript = $TestScript
Result = $Result
}}).Replace('$FolderName',$FolderName).Replace('$PullServer',"'$PullServer'")
}
<#Script ressource to create share on Pull server (locally) that contains all configurations files (ComputerName.mof, GUID.mof,
GUID.mof.checksum and ComputerName.meta.mof) and library's scripts. This share will add permission to read and execute for
everyone builtin group and Full access to the pull server computer account#>
Script AddShare {
#This ressource depends on folder existence (it won't be executed if folder doesn't exist).
DependsOn = "[Script]AddNTFSPermission"
#Create share with permissions
SetScript = $([string]{
$ShareExist = Get-WmiObject Win32_Share -filter “name=$ShareName” -ErrorAction Stop
If($ShareExist -ne $null){
$ShareExist.InvokeMethod("Delete",$null)
}
#Retrieve SID for everyone group (this method bypass operating system culture)
$Sid = New-Object System.Security.Principal.SecurityIdentifier([System.Security.Principal.WellKnownSidType]::WorldSid, $null)
[byte[]]$Ba = ,0 * $Sid.BinaryLength
[void]$Sid.GetBinaryForm($ba,0)
#Create trustee (user that will have a permission)
$Trustee = ([WMIClass] "Win32_Trustee").CreateInstance()
#Set sid
$Trustee.SID = $Ba
#Create ACE
$Ace = ([WMIClass] "Win32_ace").CreateInstance()
#Define right
$Ace.AccessMask = [System.Security.AccessControl.FileSystemRights] "Full"
$Ace.AceFlags = 0
$Ace.AceType = 0
#Define user in ACE
$Ace.Trustee = $trustee
#Create Security Descriptor containing ACE
$Sd = ([WMIClass] "Win32_SecurityDescriptor").CreateInstance()
$Sd.DACL += @($Ace.psobject.baseobject)
$Sd.ControlFlags="0x4"
#Wmi class share instance
$Shares=[wmiclass]”WIN32_Share”
#Create pull share with local path, share name and permissions (in a security descriptor)
try{
$Shares.Create($FolderName,$ShareName,0,$null,$null,$null,$Sd)
}catch{
Write-Host "Error during creation of pull share."
}
}).Replace('$FolderName',"'$FolderName'").Replace('$ShareName',"'$ShareName'")
#Test share existence and permissions (return boolean)
TestScript = $([string]{
#Retrieve pull share and her local path
$ShareExist = Get-WmiObject Win32_Share -filter “name=$ShareName” -ErrorAction Stop
If(($ShareExist -eq $null) -or ($ShareExist.Path -ne $FolderName)){
return $false
}
#Get share permissions by retrieving security settings
$SharedSecs = Get-WmiObject -Class Win32_LogicalShareSecuritySetting -Filter "Name=$ShareName"
#Get security descriptor associated to security settings
$SecDescriptor = $SharedSecs.GetSecurityDescriptor()
#Array containing all effective permissions
$EffectiveACLList = @()
#For each DACL
foreach($DACL in $SecDescriptor.Descriptor.DACL)
{
#Retrieve username
$UserName = $DACL.Trustee.Name
#Get right
$Access = $DACL.AccessMask
#Create psobject with right and user
$Obj = New-Object psobject
$ACL = $Obj | Select-Object Access, User
$ACL.Access = $Access
$ACL.User = $UserName
#Add psobject to array
$EffectiveACLList += $ACL
}
#Retrieve Everyone account
$Sid = New-Object System.Security.Principal.SecurityIdentifier([System.Security.Principal.WellKnownSidType]::WorldSid, $null)
$Everyone = ($Sid.Translate([System.Security.Principal.NTAccount])).Value
#Try to find them in effective permissions
$ValidACL = $EffectiveACLList | Where-Object {($_.User -eq $Everyone) -and ($_.Access -eq "2032127")}
#If permission is not present, return false
if(!$ValidACL){
return $false
}
return $true
}).Replace('$FolderName',"'$FolderName'").Replace('$ShareName',"'$ShareName'")
#Get share configuration (return hastable). Structure is similar to TestScript. Only return change (string instead of boolean)
GetScript = $([string]{
#Retrieve pull share
$ShareExist = Get-WmiObject Win32_Share -filter “name=$ShareName” -ErrorAction Stop
If($ShareExist -ne $null){
$Result = "Pull Server share is $ShareName. "
}else{
$Result = "Pull Server share $ShareName doesn't exist. "
return @{GetScript = $GetScript; SetScript = $SetScript; TestScript = $TestScript; Result = $Result}
}
#Retrieve local path for pull share
If($ShareExist.Path -eq $FolderName){
$Result += "Pull Server local path is $FolderName. "
}else{
$Result += "Pull Server local path is not (" + $ShareExist.path + "). "
return @{GetScript = $GetScript; SetScript = $SetScript; TestScript = $TestScript; Result = $Result}
}
#Get share permissions by retrieving security settings
$SharedSecs = Get-WmiObject -Class Win32_LogicalShareSecuritySetting -Filter "Name=$ShareName"
#Get security descriptor associated to security settings
$SecDescriptor = $SharedSecs.GetSecurityDescriptor()
#Array containing all effective permissions
$EffectiveACLList = @()
foreach($DACL in $SecDescriptor.Descriptor.DACL)
{
#Retrieve username
$UserName = $DACL.Trustee.Name
#Get right
$Access = $DACL.AccessMask
#Create psobject with right and user
$Obj = New-Object psobject
$ACL = $Obj | Select-Object Access, User
$ACL.Access = $Access
$ACL.User = $UserName
#Add psobject to array
$EffectiveACLList += $ACL
}
#Variable to check for error
$TestValidPermissions = $true
#Retrieve Everyone account
$Sid = New-Object System.Security.Principal.SecurityIdentifier([System.Security.Principal.WellKnownSidType]::WorldSid, $null)
$Everyone = ($Sid.Translate([System.Security.Principal.NTAccount])).Value
#Try to find them in effective permissions
$ValidACL = $EffectiveACLList | Where-Object {($_.User -eq $Everyone) -and ($_.Access -eq "2032127")}
#If permission is not present, return false
if(!$ValidACL){
$Result += "Share permissions is not valid for " + $Everyone + ". "
$TestValidPermissions = $false
}
#if no error has been found
if($TestValidPermissions){
$Result += "All permissions is correctly set on Pull server share."
}
return @{
GetScript = $GetScript
SetScript = $SetScript
TestScript = $TestScript
Result = $Result
}}).Replace('$FolderName',"'$FolderName'").Replace('$ShareName',"'$ShareName'")
}
<#Script ressource. It creates folder tree in pull share. 3 folders will be created in this script :
- a folder that contains configuration file generated by user
- a folder that contains Powershell functions. These functions will be used to generate LocalConfigurationManagerFile
- a folder that contains configuration with a guid in filename and a checksum file (these files will be auto generated)
#>
Script CreateFolderTree {
#This ressource depends on pull share creation
DependsOn = "[Script]AddShare"
#Create folder
SetScript = $([string]{
#Array containing all folder share
$ListFolder = @($ConfigurationFolder, $OriginConfigurationFolder, $ScriptFolder)
#Create each folder in pull server share
ForEach($Folder in $ListFolder){
New-Item -Path ("\\" + $PullServer + "\" + $ShareName + "\" + $Folder) -ItemType Directory -Force
}
}).Replace('$ShareName',"'$ShareName'").Replace('$ConfigurationFolder',"'$ConfigurationFolder'").`
Replace('$ScriptFolder',"'$ScriptFolder'").Replace('$OriginConfigurationFolder',"'$OriginConfigurationFolder'").`
Replace('$PullServer',"'$PullServer'")
#Test folder existence (return boolean)
TestScript = $([string]{
#Array containing all folder share
$ListFolder = @($ConfigurationFolder, $OriginConfigurationFolder, $ScriptFolder)
#Test existence of each folder in pull server share
ForEach($Folder in $ListFolder){
try{
Get-Item ("\\" + $PullServer + "\" + $ShareName + "\" + $Folder) -ErrorAction Stop
}catch{
return $false
}
}
return $true
}).Replace('$ShareName',"'$ShareName'").Replace('$ConfigurationFolder',"'$ConfigurationFolder'").`
Replace('$ScriptFolder',"'$ScriptFolder'").Replace('$OriginConfigurationFolder',"'$OriginConfigurationFolder'").`
Replace('$PullServer',"'$PullServer'")
#Get folder hierarchy on pull share (return hashtable)
GetScript = $([string]{
$Result = ""
#Array containing all folder share
$ListFolder = @($ConfigurationFolder, $OriginConfigurationFolder, $ScriptFolder)
ForEach($Folder in $ListFolder){
try{
#You must save result in a variable (or you will get an error)
$Item = Get-Item ("\\" + $PullServer + "\" + $ShareName + "\" + $Folder) -ErrorAction Stop
#Add string to result
$Result += "$Folder folder exists in \\" + $PullServer + "\" + $ShareName+". "
}catch{
#Add string to result
$Result += "$Folder folder doesn't exists in \\" + $PullServer + "\" + $ShareName+". "
}
}
return @{
GetScript = $GetScript
SetScript = $SetScript
TestScript = $TestScript
Result = $Result
}}).Replace('$ShareName',"'$ShareName'").Replace('$ConfigurationFolder',"'$ConfigurationFolder'").`
Replace('$ScriptFolder',"'$ScriptFolder'").Replace('$OriginConfigurationFolder',"'$OriginConfigurationFolder'").`
Replace('$PullServer',"'$PullServer'")
}
<#File ressource that copy library functions (Library Pull Configuration.ps1), to generate LocalConfigurationManager
configuration, retrieve clients computers #>
File PullScripts{
Ensure = "Present"
SourcePath = $SourceScriptPath
DestinationPath = $DestinationScriptPath
Recurse = $true
Type = "Directory"
DependsOn = "[Script]CreateFolderTree"
}
#Script ressource to generate LocalConfigurationManager file for clients computers.
Script GenerateLCM{
#Depends on PullScripts ressource
DependsOn = "[File]PullScripts"
#Generate LocalConfigurationManager configuration for all computers in a group or organizational unit
SetScript = $([string]{
#Load Library
. ($DestinationScriptPath+"\Library Pull Configuration.ps1")
#Define directory containing LocalConfigurationManager configuration file
$OutputPath = "\\"+$PullServer+"\"+$PullShare+"\LCM"
#Retrieve clients computer (see Library Pull Configuration.ps1 for description)
$Clients = Get-ClientComputer -SearchIn $FilterMode -Filter $ClientFilter
#Retrieve computers that doesn't have a LocalConfigurationManager configuration file
$Clients = $Clients | Where-Object {(Test-Path ($OutputPath+"\"+$_+".meta.mof")) -eq $false}
if($Clients -ne $null){
#Create LocalConfigurationManager configuration file
(LocalConfigurationManager -OutputPath $OutputPath -Computers $Clients -PullServer $PullServer -PullShare $PullShare `
-ConfigurationFolder $ConfigurationFolder -RefreshFrequencyMins $RefreshFrequencyMins -ConfigurationModeFrequencyMins $ConfigurationModeFrequencyMins)
}
}).Replace('$DestinationScriptPath',"'$DestinationScriptPath'").Replace('$FilterMode',"'$FilterMode'").`
Replace('$ClientFilter',"'$ClientFilter'").Replace('$PullShare',"'$PullShare'").Replace('$PullServer',"'$PullServer'").`
Replace('$ConfigurationFolder',"'$ConfigurationFolder'").Replace('$ConfigurationModeFrequencyMins',"'$ConfigurationModeFrequencyMins'").`
Replace('$RefreshFrequencyMins',"'$RefreshFrequencyMins'")
#Test presence of LocalConfigurationManager configuration file for specified computers (return boolean)
TestScript = $([string]{
#Load Library
. ($DestinationScriptPath+"\Library Pull Configuration.ps1")
#Define directory containing LocalConfigurationManager configuration file
$OutputPath = "\\"+$PullServer+"\"+$PullShare+"\LCM"
#Retrieve clients computer (see Library Pull Configuration.ps1 for description)
$Clients = Get-ClientComputer -SearchIn $FilterMode -Filter $ClientFilter
#Retrieve computers that doesn't have a LocalConfigurationManager configuration file
$Clients = $Clients | Where-Object {(Test-Path ($OutputPath+"\"+$_+".meta.mof")) -eq $false}
#If all client have a LocalConfigurationManager configuration file return true
if($Clients -eq $null){
return $true
}else{
return $false
}
}).Replace('$DestinationScriptPath',"'$DestinationScriptPath'").Replace('$FilterMode',"'$FilterMode'").`
Replace('$ClientFilter',"'$ClientFilter'").Replace('$PullShare',"'$PullShare'").Replace('$PullServer',"'$PullServer'").`
Replace('$ConfigurationFolder',"'$ConfigurationFolder'")
#Retrieve presence LocalConfigurationManager configuration files in a human readable result (return hashtable)
GetScript = $([string]{
#Load Library
. ($DestinationScriptPath+"\Library Pull Configuration.ps1")
#Define directory containing LocalConfigurationManager configuration file
$OutputPath = "\\"+$PullServer+"\"+$PullShare+"\LCM"
#Retrieve clients computer (see Library Pull Configuration.ps1 for description)
$Clients = Get-ClientComputer -SearchIn $FilterMode -Filter $ClientFilter
if($Clients -eq $null){
$Result = "There isn't clients in $ClientFilter $FilterMode. No configuration has been generated."
}
#Retrieve computers that doesn't have a LocalConfigurationManager configuration file
$ClientsNotValid = $Clients | Where-Object {(Test-Path ($OutputPath+"\"+$_+".meta.mof")) -eq $false}
#If all client have a LocalConfigurationManager configuration file return true
if($ClientsNotValid -eq $null){
$Result = "All clients in $ClientFilter $FilterMode have a valid configuration."
}else{
$Result = $ClientsNotValid -join ","
$Result += " hasn't LocalConfigurationManager (.meta.mof) file."
}
return @{
GetScript = $GetScript
SetScript = $SetScript
TestScript = $TestScript
Result = $Result
}}).Replace('$DestinationScriptPath',"'$DestinationScriptPath'").Replace('$FilterMode',"'$FilterMode'").`
Replace('$ClientFilter',"'$ClientFilter'").Replace('$PullShare',"'$PullShare'").Replace('$PullServer',"'$PullServer'").`
Replace('$ConfigurationFolder',"'$ConfigurationFolder'")
}
<#Script ressource to auto update configuration file. Retrieve files in $OriginConfiguration folder on pull server share.
Copy theses files in $ConfigurationFolder to permit clients to get her configuration. In $ConfigurationFolder, each file
will be rename with the GUID of the computer and a checksum file will be generated. When copying a file, if it already exists,
the script compares the hash and overwrite the file in $DestinationFolder if the hash is different.#>
Script AutoUpdateConfiguration{
#This ressurce depends on PullScripts ressource
DependsOn = "[File]PullScripts"
#Generate new configuration files for pull clients
SetScript = $([string]{
#Load Library
. ($DestinationScriptPath+"\Library Pull Configuration.ps1")
#Retrieve clients computer (see Library Pull Configuration.ps1 for description)
$Clients = Get-ClientComputer -SearchIn $FilterMode -Filter $ClientFilter
#Retrieve clients computers having a configuration file in $OriginConfiguration folder
$Clients = $Clients | Where-Object {(Test-Path ($OriginConfigurationPath+"\"+$_+".mof")) -eq $true}
#ForEach client that has been retrieved
ForEach($Client in $Clients){
#Get computer GUID (see Library Pull Configuration.ps1 for description)
$GUID = Get-ComputerGuid $Client
#Define configuration file path with a guid
$FileConfig = $ConfigurationPath+"\"+$GUID+".mof"
#Define full path of configuration file generated by an administrator
$OriginConfiguration = $OriginConfigurationPath+"\"+$Client+".mof"
#If file with a GUID is not present then copy the file and create a checksum
if(!(Test-Path $FileConfig)){
Copy-Item -Path $OriginConfiguration -Destination $FileConfig
New-FileChecksum -FilePath $FileConfig
}else{
#If file already exists, retrieve the hash of each file
$FileHashConfig = (Get-FileHash $OriginConfiguration).hash
$FileHashOriginConfig = [String](Get-Content ($FileConfig+".checksum"))
#If these 2 hash is different then overwrite the configuration file with a GUID and regenrate a checksum file
if($FileHashConfig -ne $FileHashOriginConfig){
Copy-Item -Path $OriginConfiguration -Destination $FileConfig
New-FileChecksum -FilePath $FileConfig
}
}
}
}).Replace('$DestinationScriptPath',"'$DestinationScriptPath'").Replace('$FilterMode',"'$FilterMode'").`
Replace('$ClientFilter',"'$ClientFilter'").Replace('$ConfigurationPath',"'$ConfigurationPath'").Replace('$PullServer',"'$PullServer'").`
Replace('$OriginConfigurationPath',"'$OriginConfigurationPath'")
#Test if updated configuration file for clients is present (for configuration that has been created by an administrator)
TestScript = $([string]{
#Load Library
. ($DestinationScriptPath+"\Library Pull Configuration.ps1")
#Retrieve clients computer (see Library Pull Configuration.ps1 for description)
$Clients = Get-ClientComputer -SearchIn $FilterMode -Filter $ClientFilter
#Retrieve clients computers having a configuration file in $OriginConfiguration folder
$Clients = $Clients | Where-Object {(Test-Path ($OriginConfigurationPath+"\"+$_+".mof")) -eq $true}
#Test variable (test result is OK as long as variable is not changed)
$Test = $true
#ForEach client that has been retrieved
ForEach($Client in $Clients){
#Get computer GUID (see Library Pull Configuration.ps1 for description)
$GUID = Get-ComputerGuid $Client
#Define configuration file path with a guid
$FileConfig = $ConfigurationPath+"\"+$GUID+".mof"
#Define full path of configuration file generated by an administrator
$OriginConfiguration = $OriginConfigurationPath+"\"+$Client+".mof"
#If file with a GUID is not present for one client, the test is failed
if(!(Test-Path $FileConfig)){
$Test = $false
break;
}else{
#If file already exists, retrieve the hash of each file
$FileHashConfig = (Get-FileHash $OriginConfiguration).hash
$FileHashOriginConfig = [String](Get-Content ($FileConfig+".checksum"))
#If these 2 hash is different, the test is failed
if($FileHashConfig -ne $FileHashOriginConfig){
$Test = $false
break;
}
}
}
return $Test
}).Replace('$DestinationScriptPath',"'$DestinationScriptPath'").Replace('$FilterMode',"'$FilterMode'").`
Replace('$ClientFilter',"'$ClientFilter'").Replace('$ConfigurationPath',"'$ConfigurationPath'").Replace('$PullServer',"'$PullServer'").`
Replace('$OriginConfigurationPath',"'$OriginConfigurationPath'")
GetScript = $([string]{
#Load Library
. ($DestinationScriptPath+"\Library Pull Configuration.ps1")
#Retrieve clients computer (see Library Pull Configuration.ps1 for description)
$Clients = Get-ClientComputer -SearchIn $FilterMode -Filter $ClientFilter
#Retrieve clients computers having a configuration file in $OriginConfiguration folder
$Clients = $Clients | Where-Object {(Test-Path ($OriginConfigurationPath+"\"+$_+".mof")) -eq $true}
#Result variable
$Result = ""
#ForEach client that has been retrieved
ForEach($Client in $Clients){
#Get computer GUID (see Library Pull Configuration.ps1 for description)
$GUID = Get-ComputerGuid $Client
#Define configuration file path with a guid
$FileConfig = $ConfigurationPath+"\"+$GUID+".mof"
#Define full path of configuration file generated by an administrator
$OriginConfiguration = $OriginConfigurationPath+"\"+$Client+".mof"
#If file with a GUID is not present for one client
if(!(Test-Path $FileConfig)){
$Result += "$Client has no configuration file. "
}else{
#If file already exists, retrieve the hash of each file
$FileHashConfig = (Get-FileHash $OriginConfiguration).hash
$FileHashOriginConfig = [String](Get-Content ($FileConfig+".checksum"))
#If these 2 hash is different, the client has an invalid configuration file
if($FileHashConfig -ne $FileHashOriginConfig){
$Result += "$Client has an invalid configuration file (difference with file in $OriginConfigurationPath). "
}
}
}
#If result has not been modified, then all configuration files is valid.
if($Result -eq ""){
$Result = "All configuration files is valid."
}
return @{
GetScript = $GetScript
SetScript = $SetScript
TestScript = $TestScript
Result = $Result
}}).Replace('$DestinationScriptPath',"'$DestinationScriptPath'").Replace('$FilterMode',"'$FilterMode'").`
Replace('$ClientFilter',"'$ClientFilter'").Replace('$ConfigurationPath',"'$ConfigurationPath'").Replace('$PullServer',"'$PullServer'").`
Replace('$OriginConfigurationPath',"'$OriginConfigurationPath'")
}
}
}
##################################################################
# Variables #
##################################################################
#Pull server name
$PullServer = "$env:Computername"
#Pull server share
$PullShare = "PullDSC"
#Pull server path
$MOFFolder = "C:\TESTDSC"
#Pull clients filter (organizational unit or AD group)
$FilterMode = "OU"
#Pull clients filter name (name of an organizational unit ou AD group) in minutes
$ClientFilter = "12FS"
#Frequency between two configuration check in minutes
$ConfigurationModeFrequencyMins = 75
#Frequency between two refresh of the configuration fil
$RefreshFrequencyMins = 30
<#Folder containing configuration file (MOF) on pull share (containing MOF file with GUID in name and checksum files).
Theses files are auto generated by configuration #>
$ConfigurationFolder = "Configuration"
#Folder containing original configuration file (MOF) on pull share that user can modify.
$OriginConfigurationFolder = "OriginalConfiguration"
#Folder containing functions on pull share (Library Pull Configuration.ps1). The file will be retrieve and use by the DSC configuration.
$ScriptFolder = "Scripts"
#Retrieve script execution path
$RootFolder = Split-Path -Path $MyInvocation.MyCommand.Path
#Folder path containing script (Library Pull Configuration.ps1) that will be retrieve by DSC file ressource on pull server.
$SourceScriptPath = "$RootFolder\$ScriptFolder\"
##################################################################
# Main #
##################################################################
#Generate configuration file for DSC pull server (smb)
DSCPullServer -Computer $PullServer -FolderName $MOFFolder -ShareName $PullShare -ClientFilter $ClientFilter `
-FilterMode $FilterMode -SourceScriptPath $SourceScriptPath -ConfigurationFolder $ConfigurationFolder `
-ConfigurationModeFrequencyMins $ConfigurationModeFrequencyMins -RefreshFrequencyMins $RefreshFrequencyMins `
-OriginConfigurationFolder $OriginConfigurationFolder -ScriptFolder $ScriptFolder -OutputPath C:\PSDSC
#Configure DSC pull server
Start-DscConfiguration "C:\PSDSC" -ComputerName $PullServer -Wait -Verbose
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment