diff --git a/Set-SslSecurity.ps1 b/Set-SslSecurity.ps1 index ca7a818..205d272 100644 --- a/Set-SslSecurity.ps1 +++ b/Set-SslSecurity.ps1 @@ -231,37 +231,13 @@ process { $ClientSaltValue = New-CCMSalt $ServiceSaltValue = New-CCMSalt - $ScriptBlock = @" -`$ClientCommunicationSalt = '$ClientSaltValue' -`$ServiceCommunicationSalt = '$ServiceSaltValue' -`$FQDN = '$SubjectWithoutCN' -`$NexusUserPW = '$NexusPw' - -# Touch NOTHING below this line -`$User = 'chocouser' -`$SecurePassword = `$NexusUserPW | ConvertTo-SecureString -AsPlainText -Force -`$RepositoryUrl = "https://`$(`$fqdn):8443/repository/ChocolateyInternal/index.json" - -`$credential = [pscredential]::new(`$user, `$securePassword) - -`$downloader = [System.Net.WebClient]::new() -`$downloader.Credentials = `$credential - -`$script = `$downloader.DownloadString("https://`$(`$FQDN):8443/repository/choco-install/ClientSetup.ps1") - -`$params = @{ - Credential = `$Credential - ClientSalt = `$ClientCommunicationSalt - ServiceSalt = `$ServiceCommunicationSalt - InternetEnabled = `$true - RepositoryUrl = `$RepositoryUrl -} - -& ([scriptblock]::Create(`$script)) @params -"@ - - $ScriptBlock | Set-Content -Path $EndpointScript + Invoke-TextReplacementInFile -Path $EndpointScript -Replacement @{ + "{{ ClientSaltValue }}" = $ClientSaltValue + "{{ ServiceSaltValue }}" = $ServiceSaltValue + "{{ FQDN }}" = $SubjectWithoutCn + } + # Agent Setup $agentArgs = @{ CentralManagementServiceUrl = "https://$($SubjectWithoutCn):24020/ChocolateyManagementService" diff --git a/modules/C4B-Environment/C4B-Environment.psm1 b/modules/C4B-Environment/C4B-Environment.psm1 index 04ff72e..dbbfee6 100644 --- a/modules/C4B-Environment/C4B-Environment.psm1 +++ b/modules/C4B-Environment/C4B-Environment.psm1 @@ -2262,6 +2262,10 @@ The host name of the C4B instance. "{{ jenkins_fqdn .*?}}" = ([uri]$Data.JenkinsUri).DnsSafeHost "{{ jenkins_port .*?}}" = ([uri]$Data.JenkinsUri).Port "{{ jenkins_password .*?}}" = [System.Web.HttpUtility]::HtmlEncode($Data.JenkinsCredential.Password.ToPlainText()) + + # Nexus Chocolatey Source Credential values + "{{ nexus_client_username .*?}}" = 'chocouser' + "{{ nexus_client_password .*?}}" = $Data.ChocoUserPassword } } } diff --git a/modules/C4B-Environment/ReadmeTemplate.html.j2 b/modules/C4B-Environment/ReadmeTemplate.html.j2 index 624eb71..d43a8fa 100644 --- a/modules/C4B-Environment/ReadmeTemplate.html.j2 +++ b/modules/C4B-Environment/ReadmeTemplate.html.j2 @@ -199,6 +199,15 @@ function CopyToClipboard(id)
📝 Note
diff --git a/scripts/ClientSetup.ps1 b/scripts/ClientSetup.ps1 index 2ce2585..ed1b176 100644 --- a/scripts/ClientSetup.ps1 +++ b/scripts/ClientSetup.ps1 @@ -14,9 +14,9 @@ param( # The credential necessary to access the internal Nexus repository. This can # be ignored if Anonymous authentication is enabled. # This parameter will be necessary if your C4B server is web-enabled. - [Parameter()] + [Parameter(Mandatory)] [pscredential] - $Credential, + $RepositoryCredential, # Specifies a target version of Chocolatey to install. By default, the # latest stable version is installed. @@ -43,17 +43,41 @@ param( # value in the Chocolatey config file [Parameter()] [string] - $ClientSalt, + $ClientCommunicationSalt, # Server salt value used to populate the centralManagementServiceCommunicationSaltAdditivePassword # value in the Chocolatey config file [Parameter()] [string] - $ServiceSalt, + $ServiceCommunicationSalt, + #Install the Chocolatey Licensed Extension with right-click context menus available [Parameter()] [Switch] - $InternetEnabled + $IncludePackageTools, + + # Allows for the application of user-defined configuration that is applied after the base configuration. + # Can override base configuration with this parameter + [Parameter()] + [Hashtable] + $AdditionalConfiguration, + + # Allows for the toggling of additonal features that is applied after the base configuration. + # Can override base configuration with this parameter + [Parameter()] + [Hashtable] + $AdditionalFeatures, + + # Allows for the installation of additional packages after the system base packages have been installed. + [Parameter()] + [Hashtable[]] + $AdditionalPackages, + + # Allows for the addition of alternative sources after the base conifguration has been applied. + # Can override base configuration with this parameter + [Parameter()] + [Hashtable[]] + $AdditionalSources ) Set-ExecutionPolicy Bypass -Scope Process -Force @@ -69,17 +93,20 @@ $params = @{ if (-not $IgnoreProxy) { if ($ProxyUrl) { + $proxy = [System.Net.WebProxy]::new($ProxyUrl, $true <#bypass on local#>) $params.Add('ProxyUrl', $ProxyUrl) } if ($ProxyCredential) { $params.Add('ProxyCredential', $ProxyCredential) + $proxy.Credentials = $ProxyCredential + } } $webClient = New-Object System.Net.WebClient -if ($Credential) { - $webClient.Credentials = $Credential.GetNetworkCredential() +if ($RepositoryCredential) { + $webClient.Credentials = $RepositoryCredential.GetNetworkCredential() } # Find the latest version of Chocolatey, if a version was not specified @@ -87,7 +114,8 @@ $NupkgUrl = if (-not $ChocolateyVersion) { $QueryUrl = (($RepositoryUrl -replace '/index\.json$'), "v3/registration/Chocolatey/index.json") -join '/' $Result = $webClient.DownloadString($QueryUrl) | ConvertFrom-Json $Result.items.items[-1].packageContent -} else { +} +else { # Otherwise, assume the URL "$($RepositoryUrl -replace '/index\.json$')/v3/content/chocolatey/$($ChocolateyVersion)/chocolatey.$($ChocolateyVersion).nupkg" } @@ -118,18 +146,19 @@ choco config set commandExecutionTimeoutSeconds 14400 # Nexus NuGet V3 Compatibility choco feature disable --name="'usePackageRepositoryOptimizations'" -if ($InternetEnabled) { - choco source add --name="'ChocolateyInternal'" --source="'$RepositoryUrl'" --allow-self-service --user="'$($Credential.UserName)'" --password="'$($Credential.GetNetworkCredential().Password)'" --priority=1 -} -else { - choco source add --name="'ChocolateyInternal'" --source="'$RepositoryUrl'" --allow-self-service --priority=1 -} - +# Environment base Source configuration +choco source add --name="'ChocolateyInternal'" --source="'$RepositoryUrl'" --allow-self-service --user="'$($RepositoryCredential.UserName)'" --password="'$($RepositoryCredential.GetNetworkCredential().Password)'" --priority=1 choco source disable --name="'Chocolatey'" choco source disable --name="'chocolatey.licensed'" choco upgrade chocolatey-license -y --source="'ChocolateyInternal'" -choco upgrade chocolatey.extension -y --params="'/NoContextMenu'" --source="'ChocolateyInternal'" --no-progress +if (-not $IncludePackageTools) { + choco upgrade chocolatey.extension -y --params="'/NoContextMenu'" --source="'ChocolateyInternal'" --no-progress +} +else { + Write-Warning "IncludePackageTools was passed. Right-Click context menus will be available for installers, .nupkg, and .nuspec file types!" + choco upgrade chocolatey.extension -y --source="'ChocolateyInternal'" --no-progress +} choco upgrade chocolateygui -y --source="'ChocolateyInternal'" --no-progress choco upgrade chocolateygui.extension -y --source="'ChocolateyInternal'" --no-progress @@ -150,11 +179,137 @@ choco feature enable --name="'usePackageHashValidation'" # CCM Check-in Configuration choco config set CentralManagementServiceUrl "https://${hostName}:24020/ChocolateyManagementService" -if ($ClientSalt) { - choco config set centralManagementClientCommunicationSaltAdditivePassword $ClientSalt +if ($ClientCommunicationSalt) { + choco config set centralManagementClientCommunicationSaltAdditivePassword $ClientCommunicationSalt } -if ($ServiceSalt) { - choco config set centralManagementServiceCommunicationSaltAdditivePassword $ServiceSalt +if ($ServiceCommunicationSalt) { + choco config set centralManagementServiceCommunicationSaltAdditivePassword $ServiceCommunicationSalt } choco feature enable --name="'useChocolateyCentralManagement'" choco feature enable --name="'useChocolateyCentralManagementDeployments'" + + +if ($AdditionalConfiguration -or $AdditionalFeatures -or $AdditionalSources -or $AdditionalPackages) { + Write-Host "Applying user supplied configuration" -ForegroundColor Cyan +} +# How we call choco from here changes as we need to be more dynamic with thingsii . +if ($AdditionalConfiguration) { + <# + We expect to pass in a hashtable with configuration information with the following shape: + + @{ + Name = BackgroundServiceAllowedCommands + Value = 'install,upgrade,uninstall' + } + #> + + $AdditionalConfiguration.GetEnumerator() | ForEach-Object { + $Config = [System.Collections.Generic.list[string]]::new() + $Config.Add('config') + $Config.Add('set') + $Config.Add("--name='$($_.Key)'") + $Config.Add("--value='$($_.Value)'") + + & choco @Config + } +} + +if ($AdditionalFeatures) { + <# + We expect to pass in feature information as a hashtable with the following shape: + + @{ + useBackgroundservice = 'Enabled' + } + #> + $AdditionalFeatures.GetEnumerator() | ForEach-Object { + + $Feature = [System.Collections.Generic.list[string]]::new() + $Feature.Add('feature') + + $state = switch ($_.Value) { + 'Enabled' { 'enable' } + 'Disabled' { 'disable' } + default { Write-Error 'State must be either Enabled or Disabled' } + } + + $Feature.Add($state) + $Feature.add("--name='$($_.Key)'") + & choco @Feature + } +} + +if ($AdditionalSources) { + + <# + We expect a user to pass in a hashtable with source information with the folllowing shape: + @{ + Name = 'MySource' + Source = 'https://nexus.fabrikam.com/repository/MyChocolateySource' + #Optional items + Credentials = $MySourceCredential + AllowSelfService = $true + AdminOnly = $true + BypassProxy = $true + Priority = 10 + Certificate = 'C:\cert.pfx' + CertificatePassword = 's0mepa$$' + } +#> + Foreach ($Source in $AdditionalSources) { + $SourceSplat = [System.Collections.Generic.List[string]]::new() + # Required items + $SourceSplat.Add('source') + $SourceSplat.Add('add') + $SourceSplat.Add("--name='$($Source.Name)'") + $SourceSplat.Add("--source='$($Source.Source)'") + + # Add credentials if source has them + if ($Source.ContainsKey('Credentials')) { + $SourceSplat.Add("--user='$($Source.Credentials.Username)'") + $SourceSplat.Add("--password='$($Source.Credentials.GetNetworkCredential().Password)'") + } + + switch ($true) { + $Source['AllowSelfService'] { $SourceSplat.add('--allow-self-service') } + $Source['AdminOnly'] { $SourceSplat.Add('--admin-only') } + $Source['BypassProxy'] { $SourceSplat.Add('--bypass-proxy') } + $Source.ContainsKey('Priority') { $SourceSplat.Add("--priority='$($Source.Priority)'") } + $Source.ContainsKey('Certificate') { $SourceSplat.Add("--cert='$($Source.Certificate)'") } + $Source.ContainsKey('CerfificatePassword') { $SourceSplat.Add("--certpassword='$($Source.CertificatePassword)'") } + } + } + + & choco @SourceSplat +} + +if ($AdditionalPackages) { + + <# + We expect to pass in a hashtable with package information with the following shape: + + @{ + Id = 'firefox' + #Optional + Version = 123.4.56 + Pin = $true + } + #> + foreach ($package in $AdditionalPackages.GetEnumerator()) { + + $PackageSplat = [System.Collections.Generic.list[string]]::new() + $PackageSplat.add('install') + $PackageSplat.add($package['Id']) + + switch ($true) { + $package.ContainsKey('Version') { $PackageSplat.Add("--version='$($package.version)'") } + $package.ContainsKey('Pin') { $PackageSplat.Add('--pin') } + } + + # Ensure packages install and they don't flood the console output + $PackageSplat.Add('-y') + $PackageSplat.Add('--no-progress') + + & choco @PackageSplat + } +} \ No newline at end of file diff --git a/scripts/New-IISCertificateHost.ps1 b/scripts/New-IISCertificateHost.ps1 index 6998813..5b10a42 100644 --- a/scripts/New-IISCertificateHost.ps1 +++ b/scripts/New-IISCertificateHost.ps1 @@ -49,12 +49,6 @@ if (-not (Get-Website -Name $siteName)) { Write-Host "Website for hosting certificate import already created" -ForegroundColor Green } -if ((Get-Website -Name 'Default Web Site')) { - Get-Website -Name 'Default Web Site' | Remove-Website -} else { - Write-Host "Default website already removed" -ForegroundColor Green -} - Write-Host "Restarting IIS to refresh bindings" -ForegroundColor Green $null = iisreset diff --git a/scripts/Register-C4bEndpoint.ps1 b/scripts/Register-C4bEndpoint.ps1 index dee0459..627c280 100644 --- a/scripts/Register-C4bEndpoint.ps1 +++ b/scripts/Register-C4bEndpoint.ps1 @@ -1,12 +1,170 @@ -# Here is a script you can run to setup your endpoints to connect to the C4B Server. -# This includes: -# - Installing Chocolatey -# - Installing your chocolatey-license -# - Running the Client Setup, which sets up Nexus repo and CCM acccess +<# + .SYNOPSIS + Deploys Chocolatey onto an endpoint. -$HostName = {{hostname}} #This needs to be the same hostname as the CN/Subject of your SSL cert + .EXAMPLE -# placeholder if using a self-signed cert + Some endpoints may require a different set of features. The default installation will apply our _recommended_ configuration. + However, you can override these defaults or enable/disable additional features by providing the `-AdditionalFeatures` parameter. -$downloader = New-Object -TypeName System.Net.WebClient -Invoke-Expression ($downloader.DownloadString("https://$($HostName):8443/repository/choco-install/ClientSetup.ps1")) \ No newline at end of file + In this example we will disable the use of the background service so non-admin users cannot use Chocolatey (not recommended), and enable Gloabl Confirmation so you no longer need to pass -y when performing a package operation. + + . .\Register-C4bEndpoint.ps1 -RepositoryCredential (Get-Credential) -AdditionalFeatures @{ useBackgroundService = 'Disabled'; allowGlobalCOnfirmation = 'Enabled' } + + .EXAMPLE + You can apply custom configuration which overrides the defaults or provides additional configuration by providing the `-AdditionalConfiguration` parameter. + The following example sets the `centralManagementReportPackagesTimerIntervalInSeconds` configuration item to 21600 seconds (6 hours). + + . .\Register-C4bEndpoint.ps1 -RepositoryCredential (Get-Credential) -AdditionalConfiguration @{ 'centralManagementReportPackagesTimerIntervalInSeconds' = '21600'} + + .EXAMPLE + You can include additional Chocolatey sources during the installation process by providing the `-AdditionalSources` parameter. + + . .\Register-C4bEndpoint.ps1 -RepositoryCredential (Get-Credential) -AdditionalSources @{Name = 'ChocolateyUpstream'; Source = 'https://community.chocolatey.org/api/v2/'} + + .EXAMPLE + This example include Packaging Tools and sets up a local folder source for package development testing. + The local folder must exist prior to using this source. + + . .\Register-C4bEndpoint.ps1 -RepositoryCredential (Get-Credential) -AdditionalSources @{Name = 'LocalTest'; Source = 'C:\packages\testing'} + + + .EXAMPLE + The following example installs the notepadplusplus.install package. + + . .\Register-C4bEndpoint.ps1 -RepositoryCredential (Get-Credential) -AdditionalPackages @{Id ='notepadplusplus.install'} + + .EXAMPLE + The following example installs version 8.7.5 of the notepadplusplus.install package. + + . .\Register-C4bEndpoint.ps1 -RepositoryCredential (Get-Credential) -AdditionalPackages @{Id ='notepadplusplus.install'; Version = '8.7.5'} + + .EXAMPLE + The following example installs version 8.7.5 of the notepadplusplus.install package and pins it so that it is not upgraded when using `choco upgrade` + To upgrade this package, you will need to first unpin it, and then perform the upgrade. + + . .\Register-C4bEndpoint.ps1 -RepositoryCredential (Get-Credential) -AdditionalPackages @{Id ='notepadplusplus.install'; Version = '8.7.5'; Pin = $true} + + .NOTES + + Full documentation is available at https://docs.chocolatey.org/en-us/c4b-environments/quick-start-environment/advanced-client-configuration/ + #> +[CmdletBinding(HelpUri = 'https://docs.chocolatey.org/en-us/c4b-environments/quick-start-environment/advanced-client-configuration/')] +Param( + # The DNS name of the server that hosts your repository, Jenkins, and Chocolatey Central Management + [Parameter()] + [String] + $Fqdn = '{{ FQDN }}', + + # Client salt value used to populate the centralManagementClientCommunicationSaltAdditivePassword + # value in the Chocolatey config file + [Parameter()] + [String] + $ClientCommunicationSalt = '{{ ClientSaltValue }}', + + # Server salt value used to populate the centralManagementServiceCommunicationSaltAdditivePassword + # value in the Chocolatey config file + [Parameter()] + [String] + $ServiceCommunicationSalt = '{{ ServiceSaltValue }}', + + [Parameter(Mandatory)] + [PSCredential] + $RepositoryCredential, + + # The URL of a proxy server to use for connecting to the repository. + [Parameter()] + [String] + $ProxyUrl, + # The credentials, if required, to connect to the proxy server. + [Parameter()] + [PSCredential] + $ProxyCredential, + + #Install the Chocolatey Licensed Extension with right-click context menus available + [Parameter()] + [Switch] + $IncludePackageTools, + + # Allows for the application of user-defined configuration that is applied after the base configuration. + # Can override base configuration with this parameter + [Parameter()] + [Hashtable] + $AdditionalConfiguration, + + # Allows for the toggling of additonal features that is applied after the base configuration. + # Can override base configuration with this parameter + [Parameter()] + [Hashtable] + $AdditionalFeatures, + + # Allows for the installation of additional packages after the system base packages have been installed. + [Parameter()] + [Hashtable[]] + $AdditionalPackages, + + # Allows for the addition of alternative sources after the base conifguration has been applied. + # Can override base configuration with this parameter + [Parameter()] + [Hashtable[]] + $AdditionalSources, + + # If passed, downloads the certificate from the client server before initializing Chocolatey Agent + [Parameter()] + [Switch] + $TrustCertificate +) + +# Touch NOTHING below this line +begin { + + $params = $PSCmdlet.MyInvocation.BoundParameters + + $commonParameters = @( + 'Verbose', 'Debug', 'ErrorAction', 'WarningAction', 'InformationAction', + 'ErrorVariable', 'WarningVariable', 'InformationVariable', 'OutVariable', 'OutBuffer', 'PipelineVariable') + + $PSCmdlet.MyInvocation.MyCommand.Parameters.Keys | ForEach-Object { + if ((-not $params.ContainsKey($_)) -and ($_ -notin $commonParameters)) { + $params[$_] = (Get-Variable -Name $_ -Scope 0 -ErrorAction SilentlyContinue).Value + } + } + # Set up our downloader + $downloader = [System.Net.WebClient]::new() + + # Setup proxy if required + if ($ProxyUrl) { + $proxy = [System.Net.WebProxy]::new($ProxyUrl, $true <#bypass on local#>) + + if ($ProxyCredential) { + $proxy.Credentials = $ProxyCredential + } + + $downloader.Proxy = $proxy + } + + $downloader.Credentials = $RepositoryCredential + +} + +end { + # If we use a Self-Signed certificate, we need to explicity trust it + if ($TrustCertificate) { + Invoke-Expression ($downloader.DownloadString("http://$($Fqdn):80/Import-ChocoServerCertificate.ps1")) + } + + # Once we trust the SSL certificate, we can start onboarding + $RepositoryUrl = "https://$($fqdn):8443/repository/ChocolateyInternal/index.json" + + foreach ($Parameter in @("FQDN", "TrustCertificate")) { + $null = $params.Remove($Parameter) + } + + $params += @{ + RepositoryUrl = $RepositoryUrl + } + + $script = $downloader.DownloadString("https://$($FQDN):8443/repository/choco-install/ClientSetup.ps1") + + & ([scriptblock]::Create($script)) @params +} \ No newline at end of file