├── .gitignore ├── Advanced-HttpsPull.ps1 ├── CompanyConfig.ps1 ├── ConfigLocalGroup.ps1 ├── DSClogs.ps1 ├── Demo-Composite.ps1 ├── DemoList.ps1 ├── FeaturesConfiguration.ps1 ├── Memberconfig.ps1 ├── PullClientLCM.ps1 ├── PushMoftoPull.ps1 ├── README.md ├── ResetDSC.ps1 ├── SamplePullConfig.ps1 ├── ServiceConfiguration.ps1 ├── companyConfig ├── DSCResources │ └── company │ │ ├── company.psd1 │ │ └── company.schema.psm1 └── companyConfig.psd1 ├── demo-named.ps1 └── demo-partial.ps1 /.gitignore: -------------------------------------------------------------------------------- 1 | scratch* 2 | dev* 3 | ~* 4 | *.bak 5 | SRV* 6 | CMSPassword.txt 7 | *(from* -------------------------------------------------------------------------------- /Advanced-HttpsPull.ps1: -------------------------------------------------------------------------------- 1 |  2 | #region define the configuration 3 | 4 | Configuration HTTPSPullserver { 5 | # Import the module that defines custom resources 6 | Import-DscResource -Module PSDesiredStateConfiguration, 7 | @{ModuleName='xPSDesiredStateConfiguration';RequiredVersion='5.0.0.0'} 8 | 9 | # Dynamically find the applicable nodes from configuration data 10 | Node $AllNodes.where{$_.Role -eq 'Web'}.NodeName { 11 | 12 | <# 13 | Install the IIS role - you might want to install the features 14 | manually to control Security 15 | #> 16 | 17 | # WindowsFeature IIS { 18 | # 19 | # Ensure = "Present" 20 | # Name = "Web-Server" 21 | # } 22 | 23 | # # Make sure the following defaults cannot be removed: 24 | 25 | WindowsFeature DefaultDoc { 26 | 27 | Ensure = "Present" 28 | Name = "Web-Default-Doc" 29 | 30 | } 31 | 32 | WindowsFeature HTTPErrors { 33 | 34 | Ensure = "Present" 35 | Name = "Web-HTTP-Errors" 36 | 37 | } 38 | 39 | WindowsFeature HTTPLogging { 40 | 41 | Ensure = "Present" 42 | Name = "Web-HTTP-Logging" 43 | 44 | } 45 | 46 | WindowsFeature StaticContent { 47 | 48 | Ensure = "Present" 49 | Name = "Web-Static-Content" 50 | 51 | } 52 | 53 | WindowsFeature RequestFiltering { 54 | 55 | Ensure = "Present" 56 | Name = "Web-Filtering" 57 | 58 | } 59 | 60 | # # Install additional IIS components to support the Web Application 61 | 62 | WindowsFeature NetExtens4 { 63 | 64 | Ensure = "Present" 65 | Name = "Web-Net-Ext45" 66 | 67 | } 68 | 69 | WindowsFeature AspNet45 { 70 | 71 | Ensure = "Present" 72 | Name = "Web-Asp-Net45" 73 | 74 | } 75 | 76 | WindowsFeature ISAPIExt { 77 | 78 | Ensure = "Present" 79 | Name = "Web-ISAPI-Ext" 80 | 81 | } 82 | 83 | WindowsFeature ISAPIFilter { 84 | 85 | Ensure = "Present" 86 | Name = "Web-ISAPI-filter" 87 | 88 | } 89 | 90 | 91 | WindowsFeature DirectoryBrowsing { 92 | 93 | Ensure = "Present" 94 | Name = "Web-Dir-Browsing" 95 | 96 | } 97 | 98 | 99 | WindowsFeature StaticCompression { 100 | 101 | Ensure = "Present" 102 | Name = "Web-Stat-Compression" 103 | 104 | } 105 | 106 | # I don't want these Additional settings for Web-Server to ever be enabled: 107 | # This list is shortened for demo purposes. I include eveything that should not be installed 108 | 109 | WindowsFeature ASP { 110 | 111 | Ensure = "Absent" 112 | Name = "Web-ASP" 113 | 114 | } 115 | 116 | WindowsFeature CGI { 117 | 118 | Ensure = "Absent" 119 | Name = "Web-CGI" 120 | 121 | } 122 | 123 | WindowsFeature IPDomainRestrictions { 124 | 125 | Ensure = "Absent" 126 | Name = "Web-IP-Security" 127 | 128 | } 129 | 130 | # !!!!! # GUI Remote Management of IIS requires the following: - people always forget this until too late 131 | 132 | WindowsFeature Management { 133 | 134 | Name = 'Web-Mgmt-Service' 135 | Ensure = 'Present' 136 | } 137 | 138 | Registry RemoteManagement { # Can set other custom settings inside this reg key 139 | 140 | Key = 'HKLM:\SOFTWARE\Microsoft\WebManagement\Server' 141 | ValueName = 'EnableRemoteManagement' 142 | ValueType = 'Dword' 143 | ValueData = '1' 144 | DependsOn = @('[WindowsFeature]Management') 145 | } 146 | 147 | Service StartWMSVC { 148 | 149 | Name = 'WMSVC' 150 | StartupType = 'Automatic' 151 | State = 'Running' 152 | DependsOn = '[Registry]RemoteManagement' 153 | 154 | } 155 | 156 | } #End Node Role Web 157 | 158 | ############################################################################### 159 | 160 | Node $AllNodes.where{$_.Role -eq 'PullServer'}.NodeName { 161 | 162 | # # This installs both, WebServer and the DSC Service for a pull server 163 | # # You could do everything manually 164 | 165 | WindowsFeature DSCServiceFeature { 166 | 167 | Ensure = "Present" 168 | Name = "DSC-Service" 169 | } 170 | 171 | xDscWebService PSDSCPullServer { 172 | 173 | Ensure = "Present" 174 | EndpointName = $Node.PullServerEndPointName 175 | Port = $Node.PullServerPort # <----------------------- Why this port? 176 | PhysicalPath = $Node.PullserverPhysicalPath 177 | CertificateThumbPrint = $Node.PullServerThumbprint # <---- Certificate Thumbprint 178 | ModulePath = $Node.PullServerModulePath 179 | ConfigurationPath = $Node.PullserverConfigurationPath 180 | State = "Started" 181 | UseSecurityBestPractices = $True 182 | DependsOn = "[WindowsFeature]DSCServiceFeature" 183 | } 184 | 185 | 186 | } # End Node PullServer 187 | 188 | } # End Config 189 | 190 | #endregion 191 | 192 | #region create configuration data 193 | $Thumbprint = Invoke-Command -Computername SRV2 -scriptblock { 194 | Get-Childitem Cert:\LocalMachine\My | 195 | Where-Object {$_.Subject -match "^CN=DSC"} | 196 | Select-Object -ExpandProperty ThumbPrint 197 | } 198 | 199 | $ConfigData=@{ 200 | # Node specific data 201 | AllNodes = @( 202 | 203 | # All Servers need following identical information 204 | @{ 205 | NodeName = '*' 206 | # PSDscAllowPlainTextPassword = $true; 207 | # PSDscAllowDomainUser = $true 208 | 209 | }, 210 | 211 | # Unique Data for each Role 212 | @{ 213 | NodeName = 'srv2.company.pri' 214 | Role = @('Web', 'PullServer') 215 | 216 | PullServerEndPointName = 'PSDSCPullServer' 217 | PullserverPort = 8080 #< - ask me why I use this port 218 | PullserverPhysicalPath = "$env:SystemDrive\inetpub\wwwroot\PSDSCPullServer" 219 | PullserverModulePath = "$env:PROGRAMFILES\WindowsPowerShell\DscService\Modules" 220 | PullServerConfigurationPath = "$env:PROGRAMFILES\WindowsPowerShell\DscService\Configuration" 221 | PullServerThumbPrint = $thumbprint 222 | 223 | } 224 | 225 | 226 | ); 227 | } 228 | 229 | #endregion 230 | 231 | #region create the config 232 | 233 | HTTPSPullserver -ConfigurationData $ConfigData -OutputPath c:\DSC\Pull 234 | 235 | #endregion 236 | 237 | #region deploy 238 | #make sure resources are deployed to the server 239 | 240 | invoke-command { get-module xpsdesiredstate* -list } -comp srv2 241 | 242 | Start-DscConfiguration -Wait -Verbose -Path c:\dsc\pull 243 | 244 | #endregion -------------------------------------------------------------------------------- /CompanyConfig.ps1: -------------------------------------------------------------------------------- 1 | #requires -version 5.0 2 | 3 | #configuration that will become a composite resource 4 | 5 | Configuration Company { 6 | 7 | Param( 8 | [ValidateNotNullorEmpty()] 9 | [string]$InterfaceAlias = 'Ethernet', 10 | [Parameter(Mandatory)] 11 | [ValidateNotNullorEmpty()] 12 | [string]$DomainName 13 | ) 14 | 15 | 16 | Import-DscResource -ModuleName PSDesiredStateConfiguration, 17 | @{ModuleName = 'xSMBShare';RequiredVersion='2.0.0.0'}, 18 | @{ModuleName = 'xWinEventLog';RequiredVersion = '1.1.0.0'}, 19 | @{ModuleName = 'xTimeZone';RequiredVersion = '1.6.0.0'}, 20 | @{ModuleName = 'xNetworking';RequiredVersion = '5.2.0.0'} 21 | 22 | #NO NODE <---------- Look Mom! 23 | 24 | File Work { 25 | DestinationPath = 'C:\Work' 26 | Type = 'Directory' 27 | Ensure = 'Present' 28 | } 29 | 30 | Service RemoteReg { 31 | Name = 'RemoteRegistry' 32 | State = 'Running' 33 | StartupType = 'Automatic' 34 | Ensure = 'Present' 35 | } 36 | 37 | WindowsFeature Backup { 38 | Name = 'Windows-Server-Backup' 39 | Ensure = 'Present' 40 | IncludeAllSubFeature = $True 41 | } 42 | 43 | xSMBShare WorkShare { 44 | DependsOn = '[File]Work' 45 | Name = 'Work$' 46 | Path = 'C:\work' 47 | FullAccess = "$DomainName\Domain Admins" 48 | FolderEnumerationMode = 'AccessBased' 49 | Ensure = 'Present' 50 | } 51 | 52 | xTimeZone TZ { 53 | IsSingleInstance = 'Yes' 54 | TimeZone = "Central Standard Time" 55 | } 56 | 57 | xWinEventLog Security { 58 | LogName = 'Security' 59 | MaximumSizeInBytes = 256MB 60 | IsEnabled = $True 61 | LogMode = 'AutoBackup' 62 | 63 | } 64 | 65 | xFirewall vmpingFWRule { 66 | Name = 'vm-monitoring-icmpv4' 67 | Action = 'Allow' 68 | Direction = 'Inbound' 69 | Enabled = $True 70 | Ensure = 'Present' 71 | InterfaceAlias = $InterfaceAlias 72 | } 73 | 74 | xFirewall SMB { 75 | Name = 'FPS-SMB-In-TCP' 76 | Action = 'Allow' 77 | Direction = 'Inbound' 78 | Enabled = $True 79 | Ensure = 'Present' 80 | InterfaceAlias = $InterfaceAlias 81 | } 82 | 83 | xFirewall RemoteEvtLogFWRule1 { 84 | Name = "RemoteEventLogSvc-In-TCP" 85 | Action = "Allow" 86 | Direction = 'Inbound' 87 | Enabled = $True 88 | Ensure = 'Present' 89 | InterfaceAlias = $InterfaceAlias 90 | } 91 | 92 | xFirewall RemoteEvtLogFWRule2 { 93 | Name = "RemoteEventLogSvc-NP-In-TCP" 94 | Action = "Allow" 95 | Direction = 'Inbound' 96 | Enabled = $True 97 | Ensure = 'Present' 98 | InterfaceAlias = $InterfaceAlias 99 | } 100 | 101 | xFirewall RemoteEvtLogFWRule3 { 102 | Name = "RemoteEventLogSvc-RPCSS-In-TCP" 103 | Action = "Allow" 104 | Direction = 'Inbound' 105 | Enabled = $True 106 | Ensure = 'Present' 107 | InterfaceAlias = $InterfaceAlias 108 | } 109 | 110 | 111 | #Enable DSC Analytic logs 112 | 113 | Script DSCAnalyticLog { 114 | 115 | DependsOn = '[xFirewall]RemoteEvtLogFWRule3' 116 | TestScript = { 117 | $status = wevtutil get-log "Microsoft-Windows-Dsc/Analytic" 118 | if ($status -contains "enabled: true") {return $True} else {return $False} 119 | } 120 | SetScript = { 121 | wevtutil.exe set-log "Microsoft-Windows-Dsc/Analytic" /q:true /e:true 122 | } 123 | getScript = { 124 | $Result = wevtutil get-log "Microsoft-Windows-Dsc/Analytic" 125 | return @{Result = $Result} 126 | } 127 | } 128 | 129 | 130 | } #end configuration -------------------------------------------------------------------------------- /ConfigLocalGroup.ps1: -------------------------------------------------------------------------------- 1 | #requires -version 5.0 2 | 3 | #create a new local 4 | 5 | configuration LocalGroup { 6 | 7 | Param() 8 | 9 | Import-DscResource -ModuleName PSDesiredStateConfiguration 10 | 11 | Node $Allnodes.NodeName { 12 | 13 | Group Localadmin { 14 | GroupName = "Administrators" 15 | Ensure = 'Present' 16 | MembersToInclude = @("company\aprils","company\help desk") 17 | psdscrunasCredential = $node.credential 18 | } 19 | 20 | LocalConfigurationManager { 21 | ActionAfterReboot = 'ContinueConfiguration' 22 | RebootNodeIfNeeded = $True 23 | ConfigurationMode = 'ApplyAndMonitor' 24 | CertificateID = $node.thumbprint #<--- LCM need to know what certificate to use 25 | RefreshMode = 'Push' 26 | } 27 | 28 | } #node 29 | 30 | } 31 | 32 | #run this script to define the configuration -------------------------------------------------------------------------------- /DSClogs.ps1: -------------------------------------------------------------------------------- 1 | Show-EventLog -ComputerName SRV3 2 | 3 | #region Using Eventlogs 4 | 5 | Get-WinEvent -ListLog *dsc*,*desired* -ComputerName SRV3 | 6 | Format-List 7 | 8 | $paramHash = @{ 9 | LogName = "Microsoft-Windows-DSC/Operational" 10 | ComputerName = "SRV3" 11 | MaxEvents = 25 12 | } 13 | 14 | Get-WinEvent @paramHash | Out-GridView 15 | 16 | #add filtering 17 | #construct a hash table for the -FilterHashTable parameter in Get-WinEvent 18 | $start = (Get-Date).AddDays(-1) 19 | $filter= @{ 20 | Logname= "Microsoft-Windows-DSC/Operational" 21 | Level=2,3 22 | StartTime= $start 23 | } 24 | 25 | #get all errors and warnings in the last 14 days 26 | Get-WinEvent -FilterHashtable $filter -Computer SRV3 | 27 | Out-GridView -Title "DSC Events" 28 | 29 | #or use XML 30 | #errors and warnings in the last 7 days 31 | $xml=@" 32 | 33 | 34 | 38 | 39 | 40 | "@ 41 | 42 | Get-WinEvent -FilterXml $xml -ComputerName SRV1 | 43 | Out-GridView 44 | 45 | #Tip: use EventViewer to build XML 46 | 47 | #enable Analytic and Debug logs 48 | #must be run locally on the server 49 | invoke-command { 50 | Wevtutil.exe set-log "Microsoft-Windows-Dsc/Analytic" /q:true /e:true 51 | Wevtutil.exe set-log "Microsoft-Windows-Dsc/Debug" /q:true /e:true 52 | } -computername SRV3 53 | 54 | <# disable 55 | 56 | invoke-command { 57 | Wevtutil.exe set-log "Microsoft-Windows-Dsc/Analytic" /q:true /e:false 58 | Wevtutil.exe set-log "Microsoft-Windows-Dsc/Debug" /q:true /e:false 59 | } -computername SRV3 60 | 61 | #> 62 | 63 | Get-WinEvent -ListLog "Microsoft-Windows-dsc/Analytic" -ComputerName SRV3 64 | 65 | #but this is a special type of log 66 | Get-WinEvent "Microsoft-Windows-dsc/Analytic" -ComputerName SRV3 -MaxEvents 25 | Out-GridView 67 | 68 | #nothing new will show up until after the log is enabled 69 | Update-DscConfiguration -ComputerName srv3 -Wait -Verbose 70 | Test-DscConfiguration -ComputerName srv3 71 | 72 | Get-WinEvent "Microsoft-Windows-dsc/Analytic" -ComputerName SRV3 -MaxEvents 25 -Oldest | 73 | Out-GridView 74 | 75 | #events grouped by job 76 | Get-DscConfiguration -CimSession SRV3 77 | 78 | $DscEvents = Get-WinEvent "Microsoft-windows-dsc/operational" -ComputerName SRV3 79 | $DscEvents += Get-WinEvent "Microsoft-Windows-Dsc/Analytic","Microsoft-Windows-Dsc/Debug" -Oldest -ComputerName SRV3 80 | $DscEvents.count 81 | 82 | #job ID 83 | $DscEvents[0].Properties[0].value 84 | #group by job Id 85 | $DSCGrouped = $DscEvents | group {$_.Properties[0].value} | sort name 86 | 87 | $DSCGrouped 88 | 89 | #sample data 90 | ($DSCGrouped[0..10]) | 91 | Format-Table -GroupBy Name -Property @{Name="Time"; 92 | Expression={$_.Group.timecreated}}, 93 | @{Name="Message";Expression={$_.Group.Message}}, 94 | @{Name="Log";Expression={$_.group.containerLog}} -Wrap 95 | 96 | #get today's events from all logs 97 | $DscEvents.where({$_.timecreated -ge (Get-Date).Date}) | 98 | sort TimeCreated | 99 | Select TimeCreated,Message | 100 | Out-GridView 101 | 102 | #find non Information events 103 | $DscEvents.where({$_.leveldisplayname -ne 'Information'}) 104 | 105 | #create a grouped hashtable based on entry type (LevelDisplayName) 106 | $DSCLevels = $DscEvents | 107 | Group LevelDisplayname -AsHashTable 108 | $DSCLevels 109 | 110 | #display sample of entries for Error 111 | $DSCLevels.error[0..10] 112 | 113 | #view errors 114 | $DSCLevels.error | Sort TimeCreated -descending | 115 | Select TimeCreated,Message | 116 | Out-GridView 117 | 118 | #endregion 119 | 120 | cls 121 | -------------------------------------------------------------------------------- /Demo-Composite.ps1: -------------------------------------------------------------------------------- 1 | #Demo using composite resource 2 | 3 | Configuration MyComposite { 4 | 5 | Param([string]$Computername) 6 | 7 | Import-DscResource -ModuleName CompanyConfig 8 | 9 | Node $Computername { 10 | 11 | #vvv this is the composite resource vvvv 12 | Company Core { 13 | DomainName = "Company.pri" 14 | InterfaceAlias = "Ethernet" 15 | } 16 | #^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 17 | 18 | service Wuauserv { 19 | Name = "Wuauserv" 20 | Ensure = 'Present' 21 | State = 'Running' 22 | StartupType = 'Automatic' 23 | } 24 | 25 | WindowsFeature Containers { 26 | Name = 'Containers' 27 | Ensure = 'Present' 28 | IncludeAllSubFeature = $True 29 | DependsOn = "[Company]Core" 30 | 31 | } 32 | 33 | LocalConfigurationManager { 34 | RebootNodeIfNeeded = $True 35 | ActionAfterReboot = 'ContinueConfiguration' 36 | AllowModuleOverwrite = $True 37 | ConfigurationMode = 'ApplyAndAutoCorrect' 38 | 39 | } #LCM 40 | 41 | } #node 42 | } #configuration 43 | 44 | 45 | MyComposite -computername SRV1 -OutputPath c:\DSC\Composite -------------------------------------------------------------------------------- /DemoList.ps1: -------------------------------------------------------------------------------- 1 | #requires -version 5.0 2 | 3 | Return "This is a demo script, Jeff! (you goober)." 4 | <# 5 | 6 | DSC Review 7 | passwords and credentials 8 | Using a secure pull server 9 | Using Partial Configurations 10 | Using Composite Configurations 11 | Using Named Configurations 12 | Reporting and Troubleshooting 13 | 14 | #> 15 | 16 | #region member configuration 17 | 18 | psedit .\Memberconfig.ps1 19 | 20 | $s = New-PSSession SRV1,SRV2 21 | #copy Resources 22 | $needed = 'xWinEventLog','xTimeZone','xNetworking','xSMBShare' 23 | 24 | foreach ($item in $s) { 25 | Split-Path (get-module $needed -ListAvailable ).ModuleBase | 26 | Copy-Item -recurse -Destination 'C:\Program Files\WindowsPowerShell\Modules' -force -Tosession $item 27 | } 28 | #verify 29 | Invoke-command { Get-module $using:needed -list } -session $s 30 | 31 | #push the LCM 32 | Set-DscLocalConfigurationManager -Path C:\dsc\Member -force -Verbose 33 | Get-DscLocalConfigurationManager -CimSession SRV1 34 | Get-DscLocalConfigurationManager -CimSession SRV2 35 | 36 | #push the configs one at a time for the sake of demonstration 37 | psedit C:\dsc\Member\SRV1.mof 38 | cls 39 | Start-DscConfiguration -Path C:\dsc\Member -ComputerName SRV1 -Force -Wait -Verbose 40 | cls 41 | Start-DscConfiguration -Path C:\dsc\Member -ComputerName SRV2 -Force -Wait -Verbose 42 | 43 | #may need to give configuration time to converge and complete 44 | Test-DscConfiguration -ComputerName SRV1,SRV2 -Verbose -Detailed 45 | 46 | #endregion 47 | 48 | #region encrypt a credential password locally 49 | 50 | Get-command -noun CMSMessage 51 | dir Cert:\CurrentUser\my -DocumentEncryptionCert 52 | 53 | #be careful about quoting 54 | $certreq = @' 55 | [Version] 56 | Signature = "$Windows NT$" 57 | 58 | [Strings] 59 | szOID_ENHANCED_KEY_USAGE = "2.5.29.37" 60 | szOID_DOCUMENT_ENCRYPTION = "1.3.6.1.4.1.311.80.1" 61 | 62 | [NewRequest] 63 | Subject = "cn=administrators@company.pri" 64 | MachineKeySet = false 65 | KeyLength = 2048 66 | KeySpec = AT_KEYEXCHANGE 67 | HashAlgorithm = Sha1 68 | Exportable = true 69 | RequestType = Cert 70 | KeyUsage = "CERT_KEY_ENCIPHERMENT_KEY_USAGE | CERT_DATA_ENCIPHERMENT_KEY_USAGE" 71 | ValidityPeriod = "Years" 72 | ValidityPeriodUnits = "1000" 73 | 74 | [Extensions] 75 | %szOID_ENHANCED_KEY_USAGE% = "{text}%szOID_DOCUMENT_ENCRYPTION%" 76 | '@ 77 | 78 | Set-Content -Value $certreq -Path C:\mycert.inf 79 | dir C:\mycert.inf 80 | 81 | #add it 82 | certreq -new C:\mycert.inf C:\mycert.cer 83 | 84 | dir Cert:\CurrentUser\my -DocumentEncryptionCert 85 | 86 | $plainPass = "P@ssw0rd" 87 | 88 | help Protect-CmsMessage -param To 89 | Protect-CMSMessage -Content $plainPass -OutFile .\CMSPassword.txt -to cn=administrators@company.pri 90 | 91 | get-content .\CMSPassword.txt 92 | 93 | get-content .\cmspassword.txt | Unprotect-CmsMessage 94 | 95 | $securePass = ConvertTo-SecureString -String $(get-content .\cmspassword.txt | Unprotect-CmsMessage) -AsPlainText -force 96 | $Cred = New-Object PSCredential "company\administrator",$securePass 97 | 98 | #prove it 99 | $cred 100 | $cred.GetNetworkCredential().Password 101 | 102 | #endregion 103 | 104 | #region encrypting passwords the right way 105 | 106 | <# 107 | Note - WMF 5 now requires an additional Enhanced Key Usage of Document Encryption. 108 | 109 | The error message will contain the following: 110 | Encryption certificates must contain the Data Encipherment or Key Encipherment key usage, and include the 111 | Document Encryption Enhanced Key Usage (1.3.6.1.4.1.311.80.1). 112 | 113 | This means that after adding the template, you will need to perform 114 | the following to add the new requirements. 115 | 1. Open MMC 116 | 2. Add Certificate Templates - pointed to ADCS 117 | 3. Open the properties of the Workstation Authentication certificate 118 | 4. Select Extensions tab 119 | 5. Edit the Application Policies and add Document Encyption 120 | 121 | # Now, on the TARGET node, get the new certificate if not using autoenrollment 122 | Invoke-Command -computername s2 {Get-Certificate -template 'workstation' -url https://dc.company.pri/ADPolicyProvider_CEP_Kerberos/service.svc/cep -CertStoreLocation Cert:\LocalMachine\My\ -Verbose} 123 | #> 124 | 125 | #request the certificate 126 | 127 | <# to do this remotely might require CredSSP or AD Delegation 128 | 129 | $server = Get-ADComputer DOM1 130 | $client = Get-ADComputer SRV3 131 | Set-ADComputer -Identity $Server -PrincipalsAllowedToDelegateToAccount $client 132 | #verify 133 | Get-ADComputer -Identity $Server -Properties PrincipalsAllowedToDelegateToAccount 134 | 135 | #need to purge tickets due to 15min SPN negative cache 136 | Invoke-Command -ComputerName $Server.Name -ScriptBlock { 137 | klist purge -li 0x3e7 138 | } 139 | #> 140 | 141 | #I created my own certificate template - you can deploy however you want 142 | Invoke-Command -computername SRV1 { 143 | Get-Certificate -template 'CompanyComputer' -url https://DOM1.company.pri/ADPolicyProvider_CEP_Kerberos/service.svc/CEP -CertStoreLocation Cert:\LocalMachine\My\ -Verbose 144 | } 145 | 146 | 147 | #get certificate and thumbprint 148 | $cert = Invoke-Command { 149 | #get server authentication certs that have not expired 150 | dir Cert:\LocalMachine\my | 151 | where {$_.EnhancedKeyUsageList.FriendlyName -contains "Document Encryption" -AND $_.notAfter -gt (Get-Date) -AND $_.Subject -eq "CN=SRV1.company.pri" } | 152 | Sort NotAfter -Descending | select -first 1 153 | } -computername SRV1 -ErrorAction Stop 154 | 155 | $cert 156 | #mkdir C:\Certs 157 | Export-Certificate -Cert $cert -FilePath C:\Certs\SRV1.cer -Force 158 | 159 | psedit .\ConfigLocalGroup.ps1 160 | 161 | $DomainCredential = Get-Credential company\administrator 162 | 163 | #add cert thumbprint to encrypt credentials 164 | $ConfigData = @{ 165 | AllNodes = @( 166 | @{ 167 | NodeName = "SRV1" 168 | Credential = $DomainCredential 169 | Thumbprint = $cert.thumbprint 170 | CertificateFile = 'C:\Certs\SRV1.cer' 171 | psDSCAllowDomainUser = $True 172 | } 173 | ) 174 | } 175 | 176 | LocalGroup -ConfigurationData $ConfigData -OutputPath C:\dsc\localGroup 177 | 178 | #view the encrypted password in the MOF 179 | psedit C:\dsc\localGroup\SRV1.mof 180 | 181 | <# 182 | instance of MSFT_Credential as $MSFT_Credential1ref 183 | { 184 | Password = "-----BEGIN CMS-----\nMIIB4wYJKoZIhvcNAQcDoIIB1DCCAdACAQAxggGLMIIBhwIBADBvMFgxFTATBgoJkiaJk/IsZAEZ\nFgVsb2NhbDEcMBoGCgmSJomT8ixkARkWDEdMT0JPTUFOVElDUzEhMB8GA1UEAxMYR0xPQk9NQU5U\nSUNTLUNISS1EQzA0LUNBAhMdAAAAtjP8M3EFNZNQAAAAAAC2MA0GCSqGSIb3DQEBBzAABIIBAEdv\nLXcX8fjklAfsxDAY9RLSz7Ad814NbJ5leUq+g38oYHYAq82DxwEhAYdod1lOHRYacIZCN/UvTSvf\nbvq0+AKQM6NZ/Ya5cg1aROBe4aJnnnQlaIspsRWPrejZDNG9DAmzVldERo6je0ZjJVinGplOMUwr\nX4ArmLhNlw7vzjll+Lpyw6ubMCN4hHltHD5U5z7VkGlyjUd1aApIv8cn1FeBI8BIgbEnOLGC3lBv\n+O8hY731tvEnYwd0WLrGnCGVkByOAYwRwIUT+ad/xfUWudkucyY/ktQ5fciqFVcaWOo64qoO4R9n\nGcVOFGWD8huw+NoDDqx0iu6iEbdT7KOBkgswPAYJKoZIhvcNAQcBMB0GCWCGSAFlAwQBKgQQXUjO\nVq5lwicyWWpYMnCHDIAQ4HcMIXywBYvHJoVGQXftmQ==\n-----END CMS-----"; 185 | UserName = "company\\administrator"; 186 | 187 | }; 188 | 189 | instance of MSFT_GroupResource as $MSFT_GroupResource1ref 190 | { 191 | ResourceID = "[Group]Localadmin"; 192 | Members = { 193 | "company\\help desk" 194 | }; 195 | Credential = $MSFT_Credential1ref; 196 | SourceInfo = "C:\\users\\jeff.company\\Documents\\Magic Briefcase\\presentations\\ITDevConnections2016\\DSCWorkshop\\demos\\ConfigLocalGroup.ps1::11::1::Group"; 197 | GroupName = "Administrators"; 198 | ModuleName = "PSDesiredStateConfiguration"; 199 | #> 200 | #before 201 | invoke-command { net localgroup administrators} -computer SRV1 202 | 203 | #push the meta config 204 | Set-DscLocalConfigurationManager -Path C:\dsc\localGroup -Verbose 205 | 206 | #check the new cert id 207 | Get-DscLocalConfigurationManager -CimSession SRV1 208 | 209 | Start-DscConfiguration -Path C:\dsc\localGroup -Verbose -wait -force 210 | 211 | invoke-command { net localgroup administrators} -computer SRV1 212 | 213 | 214 | #endregion 215 | 216 | #region setup a secure pull server 217 | 218 | #setup a DNS record and associated certificate 219 | Resolve-DnsName srv2.company.pri | Tee-object -Variable A 220 | 221 | # DNS for Pull server 222 | $paramHash = @{ 223 | ComputerName = 'DOM1' 224 | name = 'DSC' 225 | ZoneName = 'company.pri' 226 | IPv4Address = $A.IP4Address 227 | } 228 | 229 | Add-DnsServerResourceRecordA @paramHash 230 | 231 | Resolve-DnsName dsc.company.pri 232 | 233 | # Enter you target web server 234 | $ComputerName = 'SRV2' 235 | 236 | #this might require CredSSP or Kerberos delegation 237 | 238 | Invoke-Command -computername $ComputerName -scriptblock { 239 | $params = @{ 240 | template = 'WebServer2' 241 | url = 'https://DOM1.company.pri/ADPolicyProvider_CEP_Kerberos/service.svc/CEP' 242 | DnsName = "dsc.company.pri" 243 | CertStoreLocation = 'Cert:\LocalMachine\My\' 244 | SubjectName = 'CN=dsc.company.pri,OU=Servers,DC=Company,DC=Pri' 245 | Verbose = $True 246 | } 247 | Get-Certificate @params 248 | } 249 | 250 | # Can Export to PFX if needed on other web servers for high availability - Get-Help *pfx* 251 | 252 | Invoke-command -ComputerName $computername -ScriptBlock { 253 | dir Cert:\LocalMachine\My\ 254 | } 255 | 256 | psedit .\Advanced-HttpsPull.ps1 257 | 258 | enter-pssession srv2 259 | get-website 260 | exit 261 | 262 | #endregion 263 | 264 | #region pull configuration 265 | 266 | #current status 267 | Get-DscLocalConfigurationManager -CimSession SRV3 268 | #clear DSC config for the sake of the demo 269 | Remove-DscConfigurationDocument -Stage Current -CimSession srv3 270 | 271 | Get-DscConfiguration -CimSession SRV3 272 | 273 | psedit .\PullClientLCM.ps1 274 | 275 | Set-DscLocalConfigurationManager -Path C:\dsc\lcmpull -ComputerName SRV3 -Verbose 276 | Get-DscLocalConfigurationManager -CimSession SRV3 277 | 278 | psedit .\SamplePullConfig.ps1 279 | 280 | #need to rename config with guid and copy to pull server 281 | $configid = (Get-DscLocalConfigurationManager -CimSession SRV3).ConfigurationID 282 | 283 | rename-item C:\dsc\DemoPullConfig\SRV3.mof C:\dsc\DemoPullConfig\$configid.mof 284 | #it needs a checksum 285 | New-DscChecksum -Path C:\dsc\DemoPullConfig\$configid.mof 286 | 287 | dir C:\dsc\DemoPullConfig 288 | 289 | #copy the mof and checksum to the pull server 290 | $s = new-pssession SRV2 291 | 292 | dir C:\dsc\DemoPullConfig -file | 293 | copy -Destination "C:\program files\windowspowershell\dscservice\configuration" -ToSession $s 294 | 295 | Invoke-Command { dir "C:\program files\windowspowershell\dscservice\configuration" } -session $s 296 | 297 | #also need to copy resources to pull server 298 | #need to restructure folder before zipping 299 | #https://docs.microsoft.com/en-us/powershell/dsc/pullserver 300 | 301 | invoke-item 'C:\Program Files\WindowsPowerShell\Modules\xTimeZone\' 302 | 303 | $dest = "C:\DSCResources" 304 | if (-not (Test-path $dest)) { mkdir $dest} 305 | 306 | Get-DscResource | 307 | where path -match "^c:\\Program Files\\WindowsPowerShell\\Modules" | 308 | Select -ExpandProperty Module | 309 | Select Name,Version,ModuleBase -unique | 310 | foreach { 311 | $out = "{0}_{1}.zip" -f $_.Name,$_.Version 312 | $zip = Join-Path -path $dest -ChildPath $out 313 | write-host "Creating $out" -ForegroundColor green 314 | dir $_.modulebase | Compress-Archive -DestinationPath $zip -CompressionLevel Fastest -Force 315 | #give file a chance to close 316 | start-sleep -Seconds 1 317 | If (Test-Path $zip) { 318 | Try { 319 | New-DSCCheckSum -Path $zip -ErrorAction Stop -Force 320 | } 321 | Catch { 322 | Write-Warning "Failed to create checksum for $zip" 323 | } 324 | } 325 | else { 326 | Write-Warning "Failed to find $zip" 327 | } 328 | 329 | } 330 | 331 | dir $dest 332 | 333 | #copy over remoting 334 | $target = "C:\Program Files\WindowsPowerShell\DSCService\Modules" 335 | dir $dest | foreach { 336 | $_ | Copy-item -Destination $target -ToSession $s -Force 337 | } 338 | 339 | Invoke-command { dir $using:target} -session $s 340 | 341 | remove-pssession $s 342 | 343 | #clear DSCResource files 344 | Invoke-Command { 345 | dir 'C:\Program Files\WindowsPowerShell\Modules\x*' | 346 | del -Recurse -Force 347 | } -comp SRV3 348 | 349 | Invoke-command { Get-DscResource } -comp srv3 350 | 351 | #force the member server to update 352 | cls 353 | Update-DscConfiguration -Wait -computerName SRV3 -Verbose 354 | 355 | Get-DscConfiguration -CimSession srv3 356 | get-smbshare -CimSession srv3 357 | 358 | #endregion 359 | 360 | #region v5 changes 361 | #these commands don't seem to do much remotely 362 | 363 | help Publish-ModuleToPullServer -full 364 | 365 | help Publish-MOFToPullServer -full 366 | 367 | #endregion 368 | 369 | #region composite resources 370 | 371 | # https://msdn.microsoft.com/en-us/powershell/dsc/authoringresourcecomposite 372 | 373 | psedit .\companyConfig.ps1 374 | 375 | #need this file structure 376 | # \DSCResources\ 377 | # mkdir companyConfig\DSCResources\company 378 | 379 | #save as companyConfig.schema.psm1 380 | copy .\companyConfig.ps1 -Destination .\companyConfig\DSCResources\company\company.schema.psm1 -PassThru 381 | 382 | #need manifests 383 | New-ModuleManifest -Path .\companyConfig\companyConfig.psd1 384 | New-ModuleManifest -Path .\companyConfig\DSCResources\company\company.psd1 -RootModule company.schema.psm1 385 | 386 | cls 387 | dir .\companyConfig -Recurse 388 | 389 | psedit .\companyConfig\companyConfig.psd1 390 | 391 | #copy to PSModulePath 392 | copy .\companyConfig -Destination 'C:\Program Files\WindowsPowerShell\Modules' -PassThru -Recurse -Container -Force 393 | 394 | Get-DscResource company 395 | Get-DscResource company -Syntax 396 | 397 | psedit .\Demo-Composite.ps1 398 | psedit C:\dsc\Composite\SRV1.mof 399 | 400 | #deploy to pull server 401 | $guid = (New-Guid).guid 402 | LCMPull -computername SRV1 -guid $guid -output c:\dsc\lcmpull 403 | Set-DscLocalConfigurationManager -Path C:\dsc\lcmpull -ComputerName SRV1 -force 404 | 405 | psedit .\pushMoftoPull.ps1 406 | 407 | dir C:\dsc\Composite\*.mof | Push-MofToPullServer -Verbose 408 | 409 | #clear current MOFs for demo 410 | Remove-DscConfigurationDocument -Stage Pending -CimSession SRV1 411 | Remove-DscConfigurationDocument -Stage current -CimSession SRV1 412 | cls 413 | Update-DscConfiguration -Wait -computerName SRV1 -Verbose 414 | 415 | #check later after reboot and configuration has finished 416 | Get-DscConfiguration -CimSession srv1 417 | 418 | #endregion 419 | 420 | #region named configurations 421 | # https://docs.microsoft.com/en-us/powershell/dsc/pullclientconfignames 422 | psedit .\demo-named.ps1 423 | 424 | #endregion 425 | 426 | #region partial configurations 427 | 428 | #is this a technical solution for a business process problem? 429 | 430 | psedit .\demo-partial.ps1 431 | 432 | #endregion 433 | 434 | #region troubleshooting 435 | 436 | #database viewer http://www.nirsoft.net/utils/ese_database_view.html 437 | esedatabaseview 438 | 439 | help Get-DscConfigurationStatus 440 | Get-DscConfigurationStatus -CimSession srv1 441 | Get-DscConfigurationStatus -CimSession srv3 | fl 442 | 443 | Test-DscConfiguration -CimSession srv3 -Detailed 444 | 445 | #event logs 446 | psedit .\DSClogs.ps1 447 | 448 | #reporting 449 | #https://docs.microsoft.com/en-us/powershell/dsc/reportserver 450 | function Get-DSCReport { 451 | [cmdletbinding()] 452 | param( 453 | [Parameter(Position=0,Mandatory)] 454 | [string]$Computername, 455 | [string]$serviceURL = "https://DSC.company.pri:8080/PSDSCPullServer.svc" 456 | ) 457 | 458 | $AgentId = (Get-DscLocalConfigurationManager -CimSession $Computername).AgentID 459 | $requestUri = "$serviceURL/Nodes(AgentId= '$AgentId')/Reports" 460 | $params = @{ 461 | Uri = $requestUri 462 | ContentType = "application/json;odata=minimalmetadata;streaming=true;charset=utf-8" 463 | UseBasicParsing = $True 464 | Headers = @{Accept = "application/json";ProtocolVersion = "2.0"} 465 | ErrorAction = "Stop" 466 | } 467 | Try { 468 | Write-Verbose "Querying $requestUri" 469 | $request = Invoke-RestMethod @params 470 | #$object = ConvertFrom-Json $request.content 471 | $request.value 472 | } 473 | Catch { 474 | Throw $_ 475 | } 476 | } 477 | 478 | #may need to do this to handle SSL connection 479 | # $AllProtocols = [System.Net.SecurityProtocolType]'Ssl3,Tls,Tls11,Tls12' 480 | # [System.Net.ServicePointManager]::SecurityProtocol = $AllProtocols 481 | 482 | $r = Get-DSCReport -Computername srv1 -Verbose 483 | $r.count 484 | $r[0] 485 | $r[0].StatusData | ConvertFrom-Json 486 | ($r[0].StatusData | ConvertFrom-Json).ResourcesInDesiredState 487 | 488 | $r | Select JobID,@{N="Start";E={$_.StartTime -as [datetime]}}, 489 | @{N="Finish";E={$_.EndTime -as [datetime]}},OperationType,RefreshMode,Status | 490 | Out-Gridview 491 | 492 | #endregion -------------------------------------------------------------------------------- /FeaturesConfiguration.ps1: -------------------------------------------------------------------------------- 1 | #requires -version 5.0 2 | 3 | Configuration FeaturesConfiguration { 4 | 5 | Param([string[]]$Computername) 6 | 7 | Import-DscResource -ModuleName 'PSDesiredStateConfiguration' 8 | 9 | Node $Computername { 10 | 11 | WindowsFeature InternalDB { 12 | Name = 'Windows-Internal-Database' 13 | Ensure = 'Present' 14 | IncludeAllSubFeature = $True 15 | 16 | } 17 | 18 | WindowsFeature Defender { 19 | Name = 'Windows-Defender' 20 | Ensure = 'Present' 21 | } 22 | 23 | WindowsFeature TelnetClient { 24 | Name = 'Telnet-Client' 25 | Ensure = 'Present' 26 | } 27 | 28 | WindowsFeature PowerShell2 { 29 | Name = 'PowerShell-V2' 30 | Ensure = 'Absent' 31 | } 32 | 33 | WindowsFeature Wins { 34 | Name = 'Wins' 35 | Ensure = 'Absent' 36 | } 37 | 38 | WindowsFeature SNMP { 39 | Name = 'SNMP-Service' 40 | Ensure = 'Present' 41 | IncludeAllSubFeature = $True 42 | } 43 | } #node 44 | 45 | } #config 46 | 47 | FeaturesConfiguration -Computername SRV3 -OutputPath C:\dsc\PartialDemo\FeaturesConfiguration -------------------------------------------------------------------------------- /Memberconfig.ps1: -------------------------------------------------------------------------------- 1 | #requires -version 5.0 2 | 3 | Configuration Member { 4 | 5 | Param() 6 | 7 | Import-DscResource -ModuleName PSDesiredStateConfiguration, 8 | @{ModuleName = 'xSMBShare';RequiredVersion = '2.0.0.0'}, 9 | @{ModuleName = 'xWinEventLog';RequiredVersion = '1.1.0.0'}, 10 | @{ModuleName = 'xTimeZone';RequiredVersion = '1.6.0.0'}, 11 | @{ModuleName = 'xNetworking';RequiredVersion = '5.2.0.0'} 12 | 13 | Node $AllNodes.NodeName { 14 | 15 | File Work { 16 | DestinationPath = 'C:\Work' 17 | Type = 'Directory' 18 | Ensure = 'Present' 19 | } 20 | 21 | Service RemoteReg { 22 | Name = 'RemoteRegistry' 23 | State = 'Running' 24 | StartupType = 'Automatic' 25 | Ensure = 'Present' 26 | } 27 | 28 | WindowsFeature Backup { 29 | Name = 'Windows-Server-Backup' 30 | Ensure = 'Present' 31 | IncludeAllSubFeature = $True 32 | } 33 | 34 | xSMBShare WorkShare { 35 | DependsOn = '[File]Work' 36 | Name = 'Work$' 37 | Path = 'C:\work' 38 | FullAccess = 'Company\domain admins' 39 | NoAccess = 'Company\Domain Users' 40 | Ensure = 'Present' 41 | } 42 | 43 | xTimeZone TZ { 44 | IsSingleInstance = 'Yes' 45 | TimeZone = "Central Standard Time" 46 | } 47 | 48 | xWinEventLog Security { 49 | LogName = 'Security' 50 | MaximumSizeInBytes = 256MB 51 | IsEnabled = $True 52 | LogMode = 'AutoBackup' 53 | 54 | } 55 | 56 | xFirewall vmpingFWRule { 57 | Name = 'vm-monitoring-icmpv4' 58 | Action = 'Allow' 59 | Direction = 'Inbound' 60 | Enabled = $True 61 | Ensure = 'Present' 62 | InterfaceAlias = $Node.InterfaceAlias 63 | } 64 | 65 | xFirewall SMB { 66 | Name = 'FPS-SMB-In-TCP' 67 | Action = 'Allow' 68 | Direction = 'Inbound' 69 | Enabled = $True 70 | Ensure = 'Present' 71 | InterfaceAlias = $Node.InterfaceAlias 72 | } 73 | 74 | xFirewall RemoteEvtLogFWRule1 { 75 | Name = "RemoteEventLogSvc-In-TCP" 76 | Action = "Allow" 77 | Direction = 'Inbound' 78 | Enabled = $True 79 | Ensure = 'Present' 80 | InterfaceAlias = $Node.InterfaceAlias 81 | } 82 | 83 | xFirewall RemoteEvtLogFWRule2 { 84 | Name = "RemoteEventLogSvc-NP-In-TCP" 85 | Action = "Allow" 86 | Direction = 'Inbound' 87 | Enabled = $True 88 | Ensure = 'Present' 89 | InterfaceAlias = $Node.InterfaceAlias 90 | } 91 | 92 | xFirewall RemoteEvtLogFWRule3 { 93 | Name = "RemoteEventLogSvc-RPCSS-In-TCP" 94 | Action = "Allow" 95 | Direction = 'Inbound' 96 | Enabled = $True 97 | Ensure = 'Present' 98 | InterfaceAlias = $Node.InterfaceAlias 99 | } 100 | 101 | 102 | #Enable DSC Analytic logs 103 | 104 | Script DSCAnalyticLog { 105 | 106 | DependsOn = '[xFirewall]RemoteEvtLogFWRule3' 107 | TestScript = { 108 | $status = wevtutil get-log "Microsoft-Windows-Dsc/Analytic" 109 | if ($status -contains "enabled: true") {return $True} else {return $False} 110 | } 111 | SetScript = { 112 | wevtutil.exe set-log "Microsoft-Windows-Dsc/Analytic" /q:true /e:true 113 | } 114 | getScript = { 115 | $Result = wevtutil get-log "Microsoft-Windows-Dsc/Analytic" 116 | return @{Result = $Result} 117 | } 118 | } 119 | 120 | 121 | LocalConfigurationManager { 122 | RebootNodeIfNeeded = $True 123 | ActionAfterReboot = 'ContinueConfiguration' 124 | AllowModuleOverwrite = $True 125 | ConfigurationMode = 'ApplyAndAutoCorrect' 126 | 127 | } #LCM 128 | 129 | } #node 130 | 131 | Node $allnodes.Where({$_.roles -eq 'File'}).Nodename { 132 | 133 | WindowsFeature FileServices { 134 | Name = "FileAndStorage-Services" 135 | Ensure = "Present" 136 | IncludeAllSubFeature = $True 137 | } 138 | 139 | File Public { 140 | Ensure = 'Present' 141 | DestinationPath = 'C:\Public' 142 | Type = 'Directory' 143 | } 144 | 145 | File Sales { 146 | Ensure = 'Present' 147 | DestinationPath = 'C:\Sales' 148 | Type = 'Directory' 149 | } 150 | 151 | xSmbShare Public { 152 | Name = "Public" 153 | Path = "C:\Public" 154 | Ensure = 'Present' 155 | Description = "Public folder share" 156 | DependsOn = "[file]Public" 157 | fullAccess = "$($Node.Domain)\Domain Admins" 158 | ChangeAccess = "$($Node.Domain)\Domain Users" 159 | } 160 | 161 | xSmbShare Sales { 162 | Name = "Sales" 163 | Path = "C:\Sales" 164 | Ensure = 'Present' 165 | Description = "Sales department share" 166 | DependsOn = "[file]Sales" 167 | FullAccess = "$($Node.Domain)\Domain Admins" 168 | ChangeAccess = "$($Node.domain)\Sales" 169 | ReadAccess = "$($Node.Domain)\Domain Users" 170 | } 171 | } #file 172 | 173 | Node $allnodes.Where({$_.roles -eq 'Web'}).Nodename { 174 | 175 | WindowsFeature Web { 176 | Name = "Web-WebServer" 177 | Ensure = "Present" 178 | IncludeAllSubFeature = $True 179 | } 180 | WindowsFeature OData { 181 | Name = "ManagementOData" 182 | Ensure = "Present" 183 | } 184 | 185 | WindowsFeature PSWA { 186 | name = 'WindowsPowerShellWebAccess' 187 | Ensure = 'Present' 188 | DependsOn = "[WindowsFeature]Web" 189 | 190 | } 191 | } #Web 192 | 193 | } #configuration 194 | 195 | $ConfigData = @{ 196 | AllNodes = @( 197 | @{ 198 | NodeName = '*' 199 | InterfaceAlias = 'Ethernet' 200 | Domain = 'Company' 201 | }, 202 | @{NodeName = 'SRV1';Roles='File'}, 203 | @{NodeName = 'SRV2';Roles='Web'} 204 | )} 205 | 206 | #run it 207 | member -ConfigurationData $ConfigData -OutputPath c:\DSC\Member 208 | 209 | 210 | 211 | -------------------------------------------------------------------------------- /PullClientLCM.ps1: -------------------------------------------------------------------------------- 1 | #requires -version 5.0 2 | 3 | [dsclocalconfigurationmanager()] 4 | Configuration LCMPull { 5 | 6 | Param([string]$Computername,[string]$guid) 7 | 8 | Node $Computername { 9 | 10 | ConfigurationRepositoryWeb Pull { 11 | ServerURL = "https://dsc.company.pri:8080/PSDSCPullServer.svc" 12 | AllowUnsecureConnection = $False 13 | #CertificateID = "A GUID that represents the certificate used to authenticate to the server." 14 | #RegistrationKey = shared secret for named configurations 15 | #ConfigurationNames = An array of names of configurations to be pulled by the target node. 16 | } 17 | 18 | ResourceRepositoryWeb Pull { 19 | ServerURL = "https://dsc.company.pri:8080/PSDSCPullServer.svc" 20 | AllowUnsecureConnection = $False 21 | } 22 | 23 | ReportServerWeb Pull { 24 | ServerURL = "https://dsc.company.pri:8080/PSDSCPullServer.svc" 25 | AllowUnsecureConnection = $false 26 | } 27 | 28 | Settings { 29 | RebootNodeIfNeeded = $True 30 | ConfigurationMode = 'ApplyAndAutoCorrect' 31 | RefreshMode = 'Pull' 32 | ActionAfterReboot = 'ContinueConfiguration' 33 | ConfigurationID = $GUID 34 | AllowModuleOverwrite = $true 35 | } 36 | 37 | 38 | } #node 39 | 40 | 41 | } #config 42 | 43 | $guid = (New-Guid).guid 44 | 45 | LCMPull -computername SRV3 -guid $guid -output c:\dsc\lcmpull -------------------------------------------------------------------------------- /PushMoftoPull.ps1: -------------------------------------------------------------------------------- 1 | #requires -version 5.1 2 | 3 | Function Push-MofToPullServer { 4 | [cmdletbinding()] 5 | Param( 6 | [Parameter(Mandatory,Position=0,ValueFromPipelineByPropertyName)] 7 | [ValidatePattern({\.mof$})] 8 | [string]$FullName, 9 | #the name of your pull server 10 | [string]$PullServer = "SRV2" 11 | ) 12 | 13 | Begin { 14 | Write-Verbose "Creating a PSSession to $Pullserver" 15 | $sess = New-PSSession -ComputerName $PullServer 16 | } 17 | 18 | Process { 19 | Write-Verbose "Processing $fullname" 20 | $filename = Split-path $fullname -Leaf 21 | $computername = $filename.split(".")[0] 22 | Write-Verbose "Getting configuration ID from $computername" 23 | $configid = (Get-DscLocalConfigurationManager -CimSession $computername).ConfigurationID 24 | if ($configid) { 25 | $new = $fullname.replace($Computername,$configID) 26 | Write-Verbose "Renaming to $new" 27 | rename-item $fullname $new -Force 28 | Write-Verbose "Adding a checksum" 29 | New-DscChecksum -Path $new -force 30 | 31 | Write-Verbose "copy the mof and checksum to the pull server" 32 | write-verbose $new 33 | Copy-Item -Path $new -Destination "C:\program files\windowspowershell\dscservice\configuration" -ToSession $sess 34 | write-verbose "$new.checksum" 35 | Copy-Item -Path "$new.checksum" -Destination "C:\program files\windowspowershell\dscservice\configuration" -ToSession $sess 36 | } 37 | else { 38 | Write-Warning "$Computername does not appear to be configured for pull" 39 | } 40 | } 41 | 42 | End { 43 | Write-Verbose "Removing PSSession" 44 | $sess| Remove-PSsession 45 | } 46 | 47 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Advanced DSC Workshop 2 | 3 | The files in this repository are my demonstration and example files for my Advanced DSC Workshop. Nothing here should be considered production ready. The material is for educational purposes only. 4 | 5 | Content is subject to change depending on whatever conference I might be using this material. 6 | 7 | *Last updated 12 November 2017* -------------------------------------------------------------------------------- /ResetDSC.ps1: -------------------------------------------------------------------------------- 1 | #reset DSC 2 | 3 | [cmdletbinding()] 4 | Param( 5 | [Parameter(Mandatory,Position=0)] 6 | [string[]]$Computername 7 | 8 | ) 9 | 10 | [DSCLocalConfigurationManager()] 11 | configuration StandardLCM { 12 | Param([string[]]$Computername) 13 | 14 | Node $Computername { 15 | 16 | Settings { 17 | RebootNodeIfNeeded = $True 18 | ConfigurationMode = 'ApplyAndMonitor' 19 | AllowModuleOverwrite = $True 20 | RefreshMode = 'Push' 21 | } 22 | 23 | } #node 24 | } #config 25 | 26 | StandardLCM -computername $computername -output c:\dsc\standard 27 | 28 | Write-Host "Setting LCM back to default" -ForegroundColor Cyan 29 | Set-DscLocalConfigurationManager -Path C:\dsc\standard -Verbose 30 | 31 | Write-Host "Removing DSC documents" -ForegroundColor cyan 32 | Remove-DscConfigurationDocument -Stage Pending -CimSession $Computername 33 | Remove-DscConfigurationDocument -Stage Current -CimSession $Computername 34 | -------------------------------------------------------------------------------- /SamplePullConfig.ps1: -------------------------------------------------------------------------------- 1 | #requires -version 4.0 2 | 3 | Configuration DemoPullConfig { 4 | 5 | Param([string[]]$Computername) 6 | 7 | Import-DscResource -moduleName PSDesiredStateConfiguration, 8 | @{ModuleName='xNetworking';RequiredVersion = '5.2.0.0'}, 9 | @{ModuleName='xTimeZone';RequiredVersion = '1.6.0.0'}, 10 | @{ModuleName='xSMBShare';RequiredVersion = '2.0.0.0'} 11 | 12 | Node $Computername { 13 | 14 | xTimeZone Eastern { 15 | TimeZone = "Central Standard Time" 16 | IsSingleInstance = "Yes" 17 | 18 | } #end xTimeZone 19 | 20 | File Stuff { 21 | DestinationPath = "C:\Stuff" 22 | Ensure = "Present" 23 | Force = $True 24 | Type = "Directory" 25 | 26 | } #end File resource 27 | 28 | xSMBShare Stuff { 29 | Name = "Stuff$" 30 | Path = "c:\stuff" 31 | Description = "company stuff" 32 | Ensure = 'Present' 33 | FolderEnumerationMode = 'AccessBased' 34 | FullAccess = "company\domain admins" 35 | DependsOn = "[file]Stuff" 36 | } 37 | 38 | xDnsServerAddress CompanyDNS { 39 | Address = "192.168.3.10","8.8.8.8" 40 | InterfaceAlias = "Ethernet" 41 | AddressFamily = "IPv4" 42 | 43 | } #end DNSServer resource 44 | 45 | WindowsFeature SNMP { 46 | Name = "SNMP-Service" 47 | Ensure = 'Present' 48 | IncludeAllSubFeature = $True 49 | } 50 | 51 | 52 | } #end node 53 | 54 | } #close configuration 55 | 56 | 57 | DemoPullConfig -Computername SRV3 -OutputPath c:\dsc\DemoPullConfig -------------------------------------------------------------------------------- /ServiceConfiguration.ps1: -------------------------------------------------------------------------------- 1 | #requires -version 5.0 2 | 3 | Configuration ServiceConfiguration { 4 | Param([string[]]$Computername) 5 | 6 | Import-DscResource -ModuleName 'PSDesiredStateConfiguration' 7 | Node $Computername { 8 | 9 | Service RemoteRegistry { 10 | Name = 'RemoteRegistry' 11 | State = 'Running' 12 | StartupType = 'Automatic' 13 | } 14 | Service Defender { 15 | Name = 'WinDefend' 16 | State = 'Running' 17 | StartupType = 'Automatic' 18 | } 19 | 20 | Service Bits { 21 | Name = 'Bits' 22 | StartupType = 'Manual' 23 | } 24 | 25 | Service Spooler { 26 | Name = 'Spooler' 27 | State = 'Stopped' 28 | StartupType = 'Disabled' 29 | } 30 | 31 | Service SharedAccess { 32 | Name = 'SharedAccess' 33 | State = 'Stopped' 34 | StartupType = 'Disabled' 35 | } 36 | 37 | } #node 38 | 39 | 40 | } 41 | 42 | ServiceConfiguration -Computername SRV3 -OutputPath C:\dsc\PartialDemo\ServiceConfiguration 43 | -------------------------------------------------------------------------------- /companyConfig/DSCResources/company/company.psd1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jdhitsolutions/AdvancedDSCWorkshop/a1d3970fe2ca36d671777d28c154d518a32689e2/companyConfig/DSCResources/company/company.psd1 -------------------------------------------------------------------------------- /companyConfig/DSCResources/company/company.schema.psm1: -------------------------------------------------------------------------------- 1 | #requires -version 5.0 2 | 3 | #configuration that will become a composite resource 4 | 5 | Configuration Company { 6 | 7 | Param( 8 | [ValidateNotNullorEmpty()] 9 | [string]$InterfaceAlias = 'Ethernet', 10 | [Parameter(Mandatory)] 11 | [ValidateNotNullorEmpty()] 12 | [string]$DomainName 13 | ) 14 | 15 | 16 | Import-DscResource -ModuleName PSDesiredStateConfiguration, 17 | @{ModuleName = 'xSMBShare';RequiredVersion='2.0.0.0'}, 18 | @{ModuleName = 'xWinEventLog';RequiredVersion = '1.1.0.0'}, 19 | @{ModuleName = 'xTimeZone';RequiredVersion = '1.6.0.0'}, 20 | @{ModuleName = 'xNetworking';RequiredVersion = '5.2.0.0'} 21 | 22 | #NO NODE 23 | 24 | File Work { 25 | DestinationPath = 'C:\Work' 26 | Type = 'Directory' 27 | Ensure = 'Present' 28 | } 29 | 30 | Service RemoteReg { 31 | Name = 'RemoteRegistry' 32 | State = 'Running' 33 | StartupType = 'Automatic' 34 | Ensure = 'Present' 35 | } 36 | 37 | WindowsFeature Backup { 38 | Name = 'Windows-Server-Backup' 39 | Ensure = 'Present' 40 | IncludeAllSubFeature = $True 41 | } 42 | 43 | xSMBShare WorkShare { 44 | DependsOn = '[File]Work' 45 | Name = 'Work$' 46 | Path = 'C:\work' 47 | FullAccess = "$DomainName\Domain Admins" 48 | NoAccess = "$DomainName\Domain Users" 49 | Ensure = 'Present' 50 | } 51 | 52 | xTimeZone TZ { 53 | IsSingleInstance = 'Yes' 54 | TimeZone = "Central Standard Time" 55 | } 56 | 57 | xWinEventLog Security { 58 | LogName = 'Security' 59 | MaximumSizeInBytes = 256MB 60 | IsEnabled = $True 61 | LogMode = 'AutoBackup' 62 | 63 | } 64 | 65 | xFirewall vmpingFWRule { 66 | Name = 'vm-monitoring-icmpv4' 67 | Action = 'Allow' 68 | Direction = 'Inbound' 69 | Enabled = $True 70 | Ensure = 'Present' 71 | InterfaceAlias = $InterfaceAlias 72 | } 73 | 74 | xFirewall SMB { 75 | Name = 'FPS-SMB-In-TCP' 76 | Action = 'Allow' 77 | Direction = 'Inbound' 78 | Enabled = $True 79 | Ensure = 'Present' 80 | InterfaceAlias = $InterfaceAlias 81 | } 82 | 83 | xFirewall RemoteEvtLogFWRule1 { 84 | Name = "RemoteEventLogSvc-In-TCP" 85 | Action = "Allow" 86 | Direction = 'Inbound' 87 | Enabled = $True 88 | Ensure = 'Present' 89 | InterfaceAlias = $InterfaceAlias 90 | } 91 | 92 | xFirewall RemoteEvtLogFWRule2 { 93 | Name = "RemoteEventLogSvc-NP-In-TCP" 94 | Action = "Allow" 95 | Direction = 'Inbound' 96 | Enabled = $True 97 | Ensure = 'Present' 98 | InterfaceAlias = $InterfaceAlias 99 | } 100 | 101 | xFirewall RemoteEvtLogFWRule3 { 102 | Name = "RemoteEventLogSvc-RPCSS-In-TCP" 103 | Action = "Allow" 104 | Direction = 'Inbound' 105 | Enabled = $True 106 | Ensure = 'Present' 107 | InterfaceAlias = $InterfaceAlias 108 | } 109 | 110 | 111 | #Enable DSC Analytic logs 112 | 113 | Script DSCAnalyticLog { 114 | 115 | DependsOn = '[xFirewall]RemoteEvtLogFWRule3' 116 | TestScript = { 117 | $status = wevtutil get-log "Microsoft-Windows-Dsc/Analytic" 118 | if ($status -contains "enabled: true") {return $True} else {return $False} 119 | } 120 | SetScript = { 121 | wevtutil.exe set-log "Microsoft-Windows-Dsc/Analytic" /q:true /e:true 122 | } 123 | getScript = { 124 | $Result = wevtutil get-log "Microsoft-Windows-Dsc/Analytic" 125 | return @{Result = $Result} 126 | } 127 | } 128 | 129 | 130 | } #end configuration -------------------------------------------------------------------------------- /companyConfig/companyConfig.psd1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jdhitsolutions/AdvancedDSCWorkshop/a1d3970fe2ca36d671777d28c154d518a32689e2/companyConfig/companyConfig.psd1 -------------------------------------------------------------------------------- /demo-named.ps1: -------------------------------------------------------------------------------- 1 | 2 | Return "This is a walkthrough demo. Pay attention!" 3 | 4 | #region configuration 5 | 6 | Configuration NamedConfig { 7 | 8 | Param([string[]]$Computername) 9 | 10 | Import-DscResource -moduleName PSDesiredStateConfiguration, 11 | @{ModuleName="xWinEventLog";Moduleversion="1.1.0.0"} 12 | 13 | Node $Computername { 14 | 15 | File FileData { 16 | DestinationPath = "C:\Files" 17 | Ensure = "Present" 18 | Force = $True 19 | Type = "Directory" 20 | 21 | } #end File resource 22 | 23 | File Test { 24 | DestinationPath = "C:\files\Data.txt" 25 | Contents = "Lorem Ipsum Factum. Hinky Dinky Doo." 26 | DependsOn = "[file]FileData" 27 | Ensure = "Present" 28 | Type = "File" 29 | } 30 | 31 | xWinEventLog Security { 32 | LogName = "Security" 33 | IsEnabled = $True 34 | LogMode = "Retain" 35 | MaximumSizeInBytes = 1GB 36 | 37 | } 38 | 39 | Registry CompanyReg { 40 | Key = "HKey_Local_Machine\Software\CompanyData" 41 | Ensure = "present" 42 | valueName = "R-Record" 43 | ValueData = "AQ-12-CK-5684" 44 | ValueType = 'String' 45 | } 46 | 47 | Service RemoteRegistry { 48 | Name = "RemoteRegistry" 49 | Ensure = "Present" 50 | StartupType = "Disabled" 51 | State = "Stopped" 52 | } 53 | 54 | WindowsFeature InternalDB { 55 | Name = "Windows-Internal-Database" 56 | Ensure = "Present" 57 | IncludeAllSubFeature = $True 58 | } 59 | 60 | } #end node 61 | 62 | } #close configuration 63 | 64 | $computer = "SRV1" 65 | 66 | NamedConfig -computername $Computername -outputpath C:\DSC\DemoNamed 67 | 68 | #rename the mof 69 | # delete existing Named entries 70 | # dir C:\dsc\DemoNamed\named* | del 71 | rename-item C:\dsc\DemoNamed\SRV1.mof -NewName "Named.mof" -force 72 | 73 | #checksum it 74 | New-DscChecksum -Path C:\dsc\DemoNamed\named.mof -OutPath C:\dsc\DemoNamed 75 | 76 | dir c:\dsc\DemoNamed 77 | 78 | #copy to pull server 79 | $pull = "srv2" 80 | $sess = New-PSSession -ComputerName $pull 81 | 82 | #if necessary 83 | # icm {dir "C:\Program Files\WindowsPowerShell\DscService\Configuration\Named*"| del} -session $sess 84 | 85 | $paramHash = @{ 86 | Path = "c:\dsc\DemoNamed\Named*" Destination = "C:\Program Files\WindowsPowerShell\DscService\Configuration" Force = $True ToSession = $Sess } 87 | 88 | Copy-Item @paramHash 89 | #verify 90 | invoke-command { dir $using:paramhash.destination} -session $sess 91 | 92 | #endregion 93 | 94 | #region LCM 95 | 96 | #need some sort of shared secret. A GUID is probably best 97 | $myRegKey = (new-guid).guid #Get-Random -min 100000 -Maximum 250000 98 | $myRegKey 99 | 100 | #need to copy the key to the pull server 101 | 102 | Set-Content -Path c:\DSC\RegistrationKeys.txt -Value $myRegKey 103 | cat C:\DSC\RegistrationKeys.txt 104 | $paramhash.Destination = "c:\Program Files\WindowsPowerShell\DscService" 105 | $paramhash.Path = "C:\DSC\RegistrationKeys.txt" 106 | Copy-item @paramhash 107 | 108 | invoke-command -scriptblock { dir $using:paramhash.Destination } -session $sess 109 | 110 | #define a new LCM configuration 111 | [DSCLocalConfigurationManager()] 112 | Configuration DEMO_LCM_ConfigName { 113 | param 114 | ( 115 | [Parameter(Mandatory)] 116 | [string[]]$ComputerName, 117 | 118 | #[Parameter(Mandatory=$true)] 119 | #[string]$guid, <------------------------Don't need this anymore 120 | 121 | [Parameter(Mandatory)] 122 | [string]$RegistrationKey, 123 | 124 | [Parameter(Mandatory)] 125 | [string]$ThumbPrint #<---------- still need this 126 | 127 | ) 128 | Node $ComputerName { 129 | 130 | Settings { 131 | 132 | AllowModuleOverwrite = $True 133 | ConfigurationMode = 'ApplyAndAutoCorrect' 134 | RefreshMode = 'Pull' 135 | ConfigurationID = "" # Setting to blank - but can leave a guid in - won't matter 136 | } 137 | 138 | ConfigurationRepositoryWeb DSCHTTPS { 139 | ServerURL = 'https://DSC.company.pri:8080/PSDSCPullServer.svc' 140 | CertificateID = $Thumbprint 141 | AllowUnsecureConnection = $False 142 | RegistrationKey = "$RegistrationKey" # <----------- We Need this 143 | ConfigurationNames = @("Named") # <----------- The names of your configuration 144 | <# 145 | Note: If you specify more than one value in the ConfigurationNames, 146 | you must also specify PartialConfiguration blocks in your configuration. 147 | #> 148 | } 149 | ReportServerWeb CompanyPullSrv { 150 | ServerURL = 'https://DSC.company.pri:8080/PSDSCPullServer.svc' 151 | } 152 | } 153 | } 154 | 155 | $Thumbprint = Invoke-Command -Computername 'srv2' -scriptblock { 156 | Get-Childitem Cert:\LocalMachine\My | Where-Object {$_.Subject -match "^CN=DSC"} | 157 | Select-Object -ExpandProperty ThumbPrint 158 | } 159 | 160 | $computer = "SRV1" 161 | 162 | # Create the Computer.Meta.Mof in folder 163 | DEMO_LCM_ConfigName -ComputerName $computer -Thumbprint $Thumbprint -registrationKey $myRegKey -OutputPath C:\dsc\DemoNamed 164 | 165 | psedit C:\dsc\DemoNamed\SRV1.meta.mof 166 | 167 | #set the server 168 | 169 | #may need to reset it 170 | # .\ResetDSC.ps1 -Computername srv1 -Verbose 171 | # Get-DscLocalConfigurationManager -cimsession $computer 172 | 173 | Set-DscLocalConfigurationManager -cimsession $computer -Path c:\dsc\DemoNamed -verbose 174 | 175 | #endregion 176 | 177 | #region Use it 178 | 179 | Get-DSCLocalConfigurationmanager -CimSession $computer 180 | Get-DSCLocalConfigurationmanager -CimSession $computer | 181 | Select -ExpandProperty ConfigurationDownLoadManagers 182 | 183 | Update-DscConfiguration -Wait -Verbose -CimSession $computer 184 | 185 | Get-DscConfiguration -CimSession $computer 186 | 187 | dir \\$computer\c$\files 188 | get-windowsfeature -ComputerName SRV1 -Name windows-internal-database 189 | cls 190 | 191 | #endregion 192 | 193 | #clean up and reset 194 | 195 | dir C:\dsc\DemoNamed | del 196 | icm {dir "C:\Program Files\WindowsPowerShell\DscService\Configuration\named*" | del} -session $sess 197 | icm {dir "C:\Program Files\WindowsPowerShell\DscService\registrationkeys.txt" | del} -session $sess 198 | 199 | remove-pssession $sess -------------------------------------------------------------------------------- /demo-partial.ps1: -------------------------------------------------------------------------------- 1 | #requires -version 5.0 2 | 3 | #read more: https://msdn.microsoft.com/en-us/powershell/dsc/partialconfigs 4 | 5 | #configure the LCM to accept partial configurations 6 | 7 | Return "This is a demo file you silly human." 8 | 9 | #region the configurations 10 | 11 | psedit .\ServiceConfiguration.ps1 12 | psedit .\FeaturesConfiguration.ps1 13 | 14 | #endregion 15 | 16 | #region the LCM config needed to enable partials 17 | 18 | [DSCLocalConfigurationManager()] 19 | configuration PartialConfig { 20 | Param([string[]]$Computername) 21 | 22 | Node $Computername { 23 | 24 | PartialConfiguration ServiceConfiguration #<-- Name must match eventual configuration 25 | { 26 | Description = 'Configuration to configure services.' 27 | RefreshMode = 'Push' 28 | } 29 | PartialConfiguration FeaturesConfiguration #<-- Name must match eventual configuration 30 | { 31 | Description = 'Configuration for Windows Features' 32 | RefreshMode = 'Push' 33 | } 34 | 35 | Settings { 36 | RebootNodeIfNeeded = $True 37 | ConfigurationMode = 'ApplyAndAutoCorrect' 38 | AllowModuleOverwrite = $True 39 | } 40 | 41 | } #node 42 | } #config 43 | 44 | PartialConfig -Computername SRV3 -OutputPath c:\DSC\PartialDemo 45 | 46 | psedit C:\dsc\PartialDemo\SRV3.meta.mof 47 | #endregion 48 | 49 | #region deploy 50 | #wipe current config 51 | Remove-DscConfigurationDocument -Stage Current -CimSession SRV3 52 | #current LCM 53 | Get-DscLocalConfigurationManager -cimsession SRV3 54 | 55 | #push new LCM config 56 | Set-DscLocalConfigurationManager -Path C:\dsc\PartialDemo -Verbose 57 | 58 | $lcm = Get-DscLocalConfigurationManager -cimsession SRV3 59 | $lcm.PartialConfigurations 60 | 61 | #publish partial configs with Publish-DSCConfiguration 62 | Get-DscConfiguration -CimSession SRV3 63 | 64 | #enable a feature to be removed 65 | Add-WindowsFeature -Name Wins -ComputerName SRV3 66 | 67 | Publish-DscConfiguration -Path C:\dsc\PartialDemo\FeaturesConfiguration -Verbose 68 | Publish-DscConfiguration -Path C:\dsc\PartialDemo\ServiceConfiguration -Verbose 69 | 70 | #endregion 71 | 72 | #region apply with Start-DSCConfiguration and -UseExisting 73 | Start-DscConfiguration -ComputerName SRV3 -Wait -UseExisting -Verbose 74 | 75 | #wait for server to reboot if necessary 76 | 77 | Get-windowsfeature -ComputerName SRV3 | where installed 78 | Test-DscConfiguration -ComputerName SRV3 -Detailed 79 | 80 | get-service remoteregistry,windefend,bits,spooler -com srv3 | 81 | Select name,displayname,status,StartType,machinename | format-table 82 | 83 | #endregion --------------------------------------------------------------------------------