AutomatedLabWorkerADCS.psm1
#region Install-LWLabCAServers function Install-LWLabCAServers { param ( [Parameter(Mandatory = $true)][string]$ComputerName, [Parameter(Mandatory = $true)][AllowEmptyString()][string]$DomainName, [Parameter(Mandatory = $true)][string]$UserName, [Parameter(Mandatory = $true)][string]$Password, [Parameter(Mandatory = $false)][string]$ForestAdminUserName, [Parameter(Mandatory = $false)][string]$ForestAdminPassword, [Parameter(Mandatory = $false)][string]$ParentCA, [Parameter(Mandatory = $false)][string]$ParentCALogicalName, [Parameter(Mandatory = $true)][string]$CACommonName, [Parameter(Mandatory = $true)][string]$CAType, [Parameter(Mandatory = $true)][string]$KeyLength, [Parameter(Mandatory = $true)][string]$CryptoProviderName, [Parameter(Mandatory = $true)][string]$HashAlgorithmName, [Parameter(Mandatory = $true)][AllowEmptyString()][string]$DatabaseDirectory, [Parameter(Mandatory = $true)][AllowEmptyString()][string]$LogDirectory, [Parameter(Mandatory = $true)][AllowEmptyString()][string]$CpsUrl, [Parameter(Mandatory = $true)][AllowEmptyString()][string]$CpsText, [Parameter(Mandatory = $true)][boolean]$UseLDAPAIA, [Parameter(Mandatory = $true)][boolean]$UseHTTPAia, [Parameter(Mandatory = $true)][AllowEmptyString()][string]$AIAHTTPURL01, [Parameter(Mandatory = $true)][AllowEmptyString()][string]$AiaHttpUrl02, [Parameter(Mandatory = $true)][AllowEmptyString()][string]$AIAHTTPURL01UploadLocation, [Parameter(Mandatory = $true)][AllowEmptyString()][string]$AiaHttpUrl02UploadLocation, [Parameter(Mandatory = $true)][AllowEmptyString()][string]$OCSPHttpUrl01, [Parameter(Mandatory = $true)][AllowEmptyString()][string]$OCSPHttpUrl02, [Parameter(Mandatory = $true)][boolean]$UseLDAPCRL, [Parameter(Mandatory = $true)][boolean]$UseHTTPCRL, [Parameter(Mandatory = $true)][AllowEmptyString()][string]$CDPHTTPURL01, [Parameter(Mandatory = $true)][AllowEmptyString()][string]$CDPHTTPURL02, [Parameter(Mandatory = $true)][AllowEmptyString()][string]$CDPHTTPURL01UploadLocation, [Parameter(Mandatory = $true)][AllowEmptyString()][string]$CDPHTTPURL02UploadLocation, [Parameter(Mandatory = $true)][boolean]$InstallOCSP, [Parameter(Mandatory = $false)][string]$ValidityPeriod, [Parameter(Mandatory = $false)][int]$ValidityPeriodUnits, [Parameter(Mandatory = $true)][string]$CRLPeriod, [Parameter(Mandatory = $true)][int]$CRLPeriodUnits, [Parameter(Mandatory = $true)][string]$CRLOverlapPeriod, [Parameter(Mandatory = $true)][int]$CRLOverlapUnits, [Parameter(Mandatory = $true)][string]$CRLDeltaPeriod, [Parameter(Mandatory = $true)][int]$CRLDeltaPeriodUnits, [Parameter(Mandatory = $true)][string]$CertsValidityPeriod, [Parameter(Mandatory = $true)][int]$CertsValidityPeriodUnits, [Parameter(Mandatory = $true)][boolean]$InstallWebEnrollment, [Parameter(Mandatory = $true)][boolean]$InstallWebRole, [Parameter(Mandatory = $true)][boolean]$DoNotLoadDefaultTemplates, [Parameter(Mandatory = $false)][int]$PreDelaySeconds ) Write-LogFunctionEntry Install-LabWindowsFeature -ComputerName $ComputerName -FeatureName RSAT-AD-Tools -IncludeAllSubFeature -NoDisplay #region - Create parameter table $param = @{ } $param.Add('ComputerName', $ComputerName) $param.add('DomainName', $DomainName) $param.Add('UserName', $UserName) $param.Add('Password', $Password) $param.Add('ForestAdminUserName', $ForestAdminUserName) $param.Add('ForestAdminPassword', $ForestAdminPassword) $param.Add('CACommonName', $CACommonName) $param.Add('CAType', $CAType) $param.Add('CryptoProviderName', $CryptoProviderName) $param.Add('HashAlgorithmName', $HashAlgorithmName) $param.Add('KeyLength', $KeyLength) $param.Add('CertEnrollFolderPath', $CertEnrollFolderPath) $param.Add('DatabaseDirectory', $DatabaseDirectory) $param.Add('LogDirectory', $LogDirectory) $param.Add('CpsUrl', $CpsUrl) $param.Add('CpsText', """$($CpsText)""") $param.Add('UseLDAPAIA', $UseLDAPAIA) $param.Add('UseHTTPAia', $UseHTTPAia) $param.Add('AIAHTTPURL01', $AIAHTTPURL01) $param.Add('AiaHttpUrl02', $AiaHttpUrl02) $param.Add('AIAHTTPURL01UploadLocation', $AIAHTTPURL01UploadLocation) $param.Add('AiaHttpUrl02UploadLocation', $AiaHttpUrl02UploadLocation) $param.Add('OCSPHttpUrl01', $OCSPHttpUrl01) $param.Add('OCSPHttpUrl02', $OCSPHttpUrl02) $param.Add('UseLDAPCRL', $UseLDAPCRL) $param.Add('UseHTTPCRL', $UseHTTPCRL) $param.Add('CDPHTTPURL01', $CDPHTTPURL01) $param.Add('CDPHTTPURL02', $CDPHTTPURL02) $param.Add('CDPHTTPURL01UploadLocation', $CDPHTTPURL01UploadLocation) $param.Add('CDPHTTPURL02UploadLocation', $CDPHTTPURL02UploadLocation) $param.Add('InstallOCSP', $InstallOCSP) $param.Add('ValidityPeriod', $ValidityPeriod) $param.Add('ValidityPeriodUnits', $ValidityPeriodUnits) $param.Add('CRLPeriod', $CRLPeriod) $param.Add('CRLPeriodUnits', $CRLPeriodUnits) $param.Add('CRLOverlapPeriod', $CRLOverlapPeriod) $param.Add('CRLOverlapUnits', $CRLOverlapUnits) $param.Add('CRLDeltaPeriod', $CRLDeltaPeriod) $param.Add('CRLDeltaPeriodUnits', $CRLDeltaPeriodUnits) $param.Add('CertsValidityPeriod', $CertsValidityPeriod) $param.Add('CertsValidityPeriodUnits', $CertsValidityPeriodUnits) $param.Add('InstallWebEnrollment', $InstallWebEnrollment) $param.Add('InstallWebRole', $InstallWebRole) $param.Add('DoNotLoadDefaultTemplates', $DoNotLoadDefaultTemplates) #For Subordinate CAs only if ($ParentCA) { $param.add('ParentCA', $ParentCA) } if ($ParentCALogicalname) { $param.add('ParentCALogicalname', $ParentCALogicalName) } $param.Add('PreDelaySeconds', $PreDelaySeconds) #endregion - Create parameter table #region - Parameters debug Write-Debug -Message '---------------------------------------------------------------------------------------' Write-Debug -Message 'Parameters - Entered Install-LWLabCAServers' Write-Debug -Message '---------------------------------------------------------------------------------------' if ($param.GetEnumerator().count) { foreach ($key in ($param.GetEnumerator() | Sort-Object -Property Name)) { Write-Debug -message " $($key.key.padright(27)) $($key.value)" } } else { Write-Debug -message ' No parameters specified' } Write-Debug -Message '---------------------------------------------------------------------------------------' Write-Debug -Message '' #endregion - Parameters debug #region ScriptBlock for installation $caScriptBlock = { param ($param) #Make semi-sure that each install of CA server is not done at the same time Start-Sleep -Seconds $param.PreDelaySeconds Import-Module -Name ServerManager #region - Check if CA is already installed if ((Get-WindowsFeature -Name 'ADCS-Cert-Authority').Installed) { Write-Output "A Certificate Authority is already installed on '$($param.ComputerName)'. Skipping installation." return } #endregion #region - Create CAPolicy file $caPolicyFileName = "$Env:Windir\CAPolicy.inf" Set-Content $caPolicyFileName -Force -Value ';CAPolicy for CA' Add-Content $caPolicyFileName -Value '; Please replace sample CPS OID with your own OID' Add-Content $caPolicyFileName -Value '' Add-Content $caPolicyFileName -Value '[Version]' Add-Content $caPolicyFileName -Value "Signature=`"`$Windows NT`$`" " Add-Content $caPolicyFileName -Value '' Add-Content $caPolicyFileName -Value '[PolicyStatementExtension]' Add-Content $caPolicyFileName -Value 'Policies=LegalPolicy' Add-Content $caPolicyFileName -Value 'Critical=0' Add-Content $caPolicyFileName -Value '' Add-Content $caPolicyFileName -Value '[LegalPolicy]' Add-Content $caPolicyFileName -Value 'OID=1.3.6.1.4.1.11.21.43' Add-Content $caPolicyFileName -Value "Notice=$($param.CpsText)" Add-Content $caPolicyFileName -Value "URL=$($param.CpsUrl)" Add-Content $caPolicyFileName -Value '' Add-Content $caPolicyFileName -Value '[Certsrv_Server]' Add-Content $caPolicyFileName -Value 'ForceUTF8=true' Add-Content $caPolicyFileName -Value "RenewalKeyLength=$($param.KeyLength)" Add-Content $caPolicyFileName -Value "RenewalValidityPeriod=$($param.ValidityPeriod)" Add-Content $caPolicyFileName -Value "RenewalValidityPeriodUnits=$($param.ValidityPeriodUnits)" Add-Content $caPolicyFileName -Value "CRLPeriod=$($param.CRLPeriod)" Add-Content $caPolicyFileName -Value "CRLPeriodUnits=$($param.CRLPeriodUnits)" Add-Content $caPolicyFileName -Value "CRLDeltaPeriod=$($param.CRLDeltaPeriod)" Add-Content $caPolicyFileName -Value "CRLDeltaPeriodUnits=$($param.CRLDeltaPeriodUnits)" Add-Content $caPolicyFileName -Value 'EnableKeyCounting=0' Add-Content $caPolicyFileName -Value 'AlternateSignatureAlgorithm=0' if ($param.DoNotLoadDefaultTemplates) { Add-Content $caPolicyFileName -Value 'LoadDefaultTemplates=0' } if ($param.CAType -like '*root*') { Add-Content $caPolicyFileName -Value '' Add-Content $caPolicyFileName -Value '[Extensions]' Add-Content $caPolicyFileName -Value ';Remove CA Version Index' Add-Content $caPolicyFileName -Value '1.3.6.1.4.1.311.21.1=' Add-Content $caPolicyFileName -Value ';Remove CA Hash of previous CA Certificates' Add-Content $caPolicyFileName -Value '1.3.6.1.4.1.311.21.2=' Add-Content $caPolicyFileName -Value ';Remove V1 Certificate Template Information' Add-Content $caPolicyFileName -Value '1.3.6.1.4.1.311.20.2=' Add-Content $caPolicyFileName -Value ';Remove CA of V2 Certificate Template Information' Add-Content $caPolicyFileName -Value '1.3.6.1.4.1.311.21.7=' Add-Content $caPolicyFileName -Value ';Key Usage Attribute set to critical' Add-Content $caPolicyFileName -Value '2.5.29.15=AwIBBg==' Add-Content $caPolicyFileName -Value 'Critical=2.5.29.15' } if ($param.DebugPref -eq 'Continue') { $file = get-content -Path "$Env:Windir\CAPolicy.inf" Write-Debug -Message 'CApolicy.inf contents:' foreach ($line in $file) { Write-Debug -Message $line } } #endregion - Create CAPolicy file #region - Install CA $hostOSVersion = [system.version](Get-WmiObject -Class Win32_OperatingSystem).Version if ($hostOSVersion -ge [system.version]'6.2') { $InstallFeatures = 'Import-Module -Name ServerManager; Add-WindowsFeature -IncludeManagementTools -Name ADCS-Cert-Authority' } else { $InstallFeatures = 'Import-Module -Name ServerManager; Add-WindowsFeature -Name ADCS-Cert-Authority' } # OCSP not yet supported #if ($param.InstallOCSP) { $InstallFeatures += ", ADCS-Online-Cert" } if ($param.InstallWebEnrollment) { $InstallFeatures += ', ADCS-Web-Enrollment' } if ($param.ForestAdminUserName) { Write-Verbose -Message "ForestAdminUserName=$($param.ForestAdminUserName), ForestAdminPassword=$($param.ForestAdminPassword)" if ($param.DebugPref -eq 'Continue') { Write-Verbose -Message "Adding $($param.ForestAdminUserName) to local administrators group" Write-Verbose -Message "WinNT:://$($param.ForestAdminUserName.replace('\', '/'))" } $localGroup = ([ADSI]'WinNT://./Administrators,group') $localGroup.psbase.Invoke('Add', ([ADSI]"WinNT://$($param.ForestAdminUserName.replace('\', '/'))").path) Write-Verbose -Message "Check 2c -create credential of ""$($param.ForestAdminUserName)"" and ""$($param.ForestAdminPassword)""" $forestAdminCred = (New-Object System.Management.Automation.PSCredential($param.ForestAdminUserName, ($param.ForestAdminPassword | ConvertTo-SecureString -AsPlainText -Force))) } else { Write-Verbose -Message 'No ForestAdminUserName!' } Write-Verbose -Message 'Installing roles and features now' Write-Verbose -Message "Command: $InstallFeatures" Invoke-Expression -Command ($InstallFeatures += " -Confirm:`$false") | Out-Null Write-Verbose -Message 'Installing ADCS now' $installCommand = 'Install-AdcsCertificationAuthority ' $installCommand += "-CACommonName $($param.CACommonName) " $installCommand += "-CAType $($param.CAType) " $installCommand += "-KeyLength $($param.KeyLength) " $installCommand += "-CryptoProviderName ""$($param.CryptoProviderName)"" " $installCommand += "-HashAlgorithmName ""$($param.HashAlgorithmName)"" " $installCommand += '-OverwriteExistingKey ' $installCommand += '-OverwriteExistingDatabase ' $installCommand += '-Force ' $installCommand += '-Confirm:$false ' if ($forestAdminCred) { $installCommand += '-Credential $forestAdminCred ' } if ($param.DatabaseDirectory) { $installCommand += "-DatabaseDirectory $($param.DatabaseDirectory) " } if ($param.LogDirectory) { $installCommand += "-LogDirectory $($param.LogDirectory) " } if ($param.CAType -like '*root*') { $installCommand += "-ValidityPeriod $($param.ValidityPeriod) " $installCommand += "-ValidityPeriodUnits $($param.ValidityPeriodUnits) " } else { $installCommand += "-ParentCA $($param.ParentCA)`\$($param.ParentCALogicalName) " } $installCommand += ' | Out-Null' if ($param.DebugPref -eq 'Continue') { Write-Debug -Message 'Install command:' Write-Debug -Message $installCommand Set-Content -Path 'C:\debug-CAinst.txt' -value $installCommand } Invoke-Expression -Command $installCommand if ($param.ForestAdminUserName) { if ($param.DebugPref -eq 'Continue') { Write-Debug -Message "Removing $($param.ForestAdminUserName) to local administrators group" } $localGroup = ([ADSI]'WinNT://./Administrators,group') $localGroup.psbase.Invoke('Remove', ([ADSI]"WinNT://$($param.ForestAdminUserName.replace('\', '/'))").path) } if ($param.InstallWebEnrollment) { Write-Verbose -Message 'Installing Web Enrollment service now' Install-ADCSWebEnrollment -Confirm:$False | Out-Null } if ($param.InstallWebRole) { if (!(Get-WindowsFeature -Name 'web-server')) { Add-WindowsFeature -Name 'Web-Server' -IncludeManagementTools #Allow "+" characters in URL for supporting delta CRLs Set-WebConfiguration -Filter system.webServer/security/requestFiltering -PSPath 'IIS:\sites\Default Web Site' -Value @{allowDoubleEscaping=$true} } } #endregion - Install CA #region - Configure IIS virtual directories if ($param.UseHTTPAia) { New-WebVirtualDirectory -Site 'Default Web Site' -Name Aia -PhysicalPath 'C:\Windows\System32\CertSrv\CertEnroll' | Out-Null New-WebVirtualDirectory -Site 'Default Web Site' -Name Cdp -PhysicalPath 'C:\Windows\System32\CertSrv\CertEnroll' | Out-Null } #endregion - Configure IIS virtual directories #region - Configure OCSP <# OCSP not yet supported if ($InstallOCSP) { Write-Verbose -Message "Installing Online Responder" Install-ADCSOnlineResponder -Force | Out-Null } #> #endregion - Configure OCSP #region - Configure CA function Invoke-CustomExpression { param ($Command) Write-Host $command Invoke-Expression -Command $command } #Declare configuration NC if ($param.CAType -like 'Enterprise*') { $lDAPname = '' foreach ($part in ($param.DomainName.split('.'))) { $lDAPname += ",DC=$part" } Invoke-CustomExpression -Command "certutil -setreg CA\DSConfigDN ""CN=Configuration$lDAPname""" } #Apply the required CDP Extension URLs $command = "certutil -setreg CA\CRLPublicationURLs ""1:$($Env:WinDir)\system32\CertSrv\CertEnroll\%3%8%9.crl" if ($param.UseLDAPCRL) { $command += '\n11:ldap:///CN=%7%8,CN=%2,CN=CDP,CN=Public Key Services,CN=Services,%6%10' } if ($param.UseHTTPCRL) { $command += "\n2:$($param.CDPHTTPURL01)/%3%8%9.crl" } if ($param.CDPHTTPURL01UploadLocation) { $command += "\n1:$($param.CDPHTTPURL01UploadLocation)/%3%8%9.crl" } $command += '"' Invoke-CustomExpression -Command $command #Apply the required AIA Extension URLs $command = "certutil -setreg CA\CACertPublicationURLs ""1:$($Env:WinDir)\system3\CertSrv\CertEnroll\%1_%3%4.crt" if ($param.UseLDAPAia) { $command += '\n3:ldap:///CN=%7,CN=AIA,CN=Public Key Services,CN=Services,%6%11' } if ($param.UseHTTPAia) { $command += "\n2:$($param.AIAHTTPURL01)/%1_%3%4.crt" } if ($param.AIAHTTPURL01UploadLocation) { $command += "\n1:$($param.AIAHTTPURL01UploadLocation)/%3%8%9.crl" } <# OCSP not yet supported if ($param.InstallOCSP -and $param.OCSPHttpUrl01) { $Line += "\n34:$($param.OCSPHttpUrl01)" } if ($param.InstallOCSP -and $param.OCSPHttpUrl02) { $Line += "\n34:$($param.OCSPHttpUrl02)" } #> $command += '"' Invoke-CustomExpression -Command $command #Define default maximum certificate lifetime for issued certificates Invoke-CustomExpression -Command "certutil -setreg ca\ValidityPeriodUnits $($param.CertsValidityPeriodUnits)" Invoke-CustomExpression -Command "certutil -setreg ca\ValidityPeriod ""$($param.CertsValidityPeriod)""" #Define CRL Publication Intervals Invoke-CustomExpression -Command "certutil -setreg CA\CRLPeriodUnits $($param.CRLPeriodUnits)" Invoke-CustomExpression -Command "certutil -setreg CA\CRLPeriod ""$($param.CRLPeriod)""" #Define CRL Overlap Invoke-CustomExpression -Command "certutil -setreg CA\CRLOverlapUnits $($param.CRLOverlapUnits)" Invoke-CustomExpression -Command "certutil -setreg CA\CRLOverlapPeriod ""$($param.CRLOverlapPeriod)""" #Define Delta CRL Invoke-CustomExpression -Command "certutil -setreg CA\CRLDeltaUnits $($param.CRLDeltaPeriodUnits)" Invoke-CustomExpression -Command "certutil -setreg CA\CRLDeltaPeriod ""$($param.CRLDeltaPeriod)""" #Enable Auditing Logging Invoke-CustomExpression -Command 'certutil -setreg CA\Auditfilter 0x7F' #Enable UTF-8 Encoding Invoke-CustomExpression -Command 'certutil -setreg ca\forceteletex +0x20' if ($param.CAType -like '*root*') { #Disable Discrete Signatures in Subordinate Certificates (WinXP KB968730) Invoke-CustomExpression -Command 'certutil -setreg CA\csp\AlternateSignatureAlgorithm 0' #Force digital signature removal in KU for cert issuance (see also kb888180) Invoke-CustomExpression -Command 'certutil -setreg policy\EditFlags -EDITF_ADDOLDKEYUSAGE' #Enable SAN Invoke-CustomExpression -Command 'certutil -setreg policy\EditFlags +EDITF_ATTRIBUTESUBJECTALTNAME2' #Configure policy module to automatically issue certificates when requested Invoke-CustomExpression -Command 'certutil -setreg ca\PolicyModules\CertificateAuthority_MicrosoftDefault.Policy\RequestDisposition 1' } #If CA is Root CA and Sub CAs are present, disable (do not publish) templates (except SubCA template) if ($param.DoNotLoadDefaultTemplates) { Invoke-CustomExpression -Command 'certutil -SetCATemplates +SubCA' } #endregion - Configure CA #region - Restart of CA if ((Get-Service -Name 'CertSvc').Status -eq 'Running') { Write-Verbose -Message 'Stopping ADCS Service' $totalretries = 5 $retries = 0 do { Stop-Service -Name 'CertSvc' -ErrorAction SilentlyContinue if ((Get-Service -Name 'CertSvc').Status -ne 'Stopped') { $retries++ Start-Sleep -Seconds 1 } } until (((Get-Service -Name 'CertSvc').Status -eq 'Stopped') -or ($retries -ge $totalretries)) if ((Get-Service -Name 'CertSvc').Status -eq 'Stopped') { Write-Verbose -Message 'ADCS service is now stopped' } else { Write-Error -Message 'Could not stop ADCS Service after several retries' return } } Write-Verbose -Message 'Starting ADCS Service now' $totalretries = 5 $retries = 0 do { Start-Service -Name 'CertSvc' -ErrorAction SilentlyContinue if ((Get-Service -Name 'CertSvc').Status -ne 'Running') { $retries++ Start-Sleep -Seconds 1 } } until (((Get-Service -Name 'CertSvc').Status -eq 'Running') -or ($retries -ge $totalretries)) if ((Get-Service -Name 'CertSvc').Status -eq 'Running') { Write-Verbose -Message 'ADCS service is now started' } else { Write-Error -Message 'Could not start ADCS Service after several retries' return } #endregion - Restart of CA Write-Verbose -Message 'Waiting for admin interface to be ready' $totalretries = 10 $retries = 0 do { $result = Invoke-Expression -Command "certutil -pingadmin .\$($param.CACommonName)" if (!($result | Where-Object { $_ -like '*interface is alive*' })) { $retries++ Write-Verbose -Message "Admin interface not ready. Check $retries of $totalretries" if ($retries -lt $totalretries) { Start-Sleep -Seconds 10 } } } until (($result | Where-Object { $_ -like '*interface is alive*' }) -or ($retries -ge $totalretries)) if ($result | Where-Object { $_ -like '*interface is alive*' }) { Write-Verbose -Message 'Admin interface is now ready' } else { Write-Error -Message 'Admin interface was not ready after several retries' return } #region - Issue of CRL Start-Sleep -Seconds 2 Invoke-Expression -Command 'certutil -crl' | Out-Null $totalretries = 12 $retries = 0 do { Start-Sleep -Seconds 5 $retries++ } until ((Get-ChildItem "$env:systemroot\system32\CertSrv\CertEnroll\*.crl") -or ($retries -ge $totalretries)) #endregion - Issue of CRL if (($param.CAType -like 'Enterprise*') -and ($param.DoNotLoadDefaultTemplates)) { Invoke-Expression 'certutil -SetCATemplates +SubCA' } } #endregion Write-Verbose -Message "Performing installation of $($param.CAType) on '$($param.ComputerName)'" $cred = (New-Object System.Management.Automation.PSCredential($param.UserName, ($param.Password | ConvertTo-SecureString -AsPlainText -Force))) $caSession = New-LabPSSession -ComputerName $param.ComputerName $Job = Invoke-Command -Session $caSession -Scriptblock $caScriptBlock -ArgumentList $param -AsJob -JobName "Install CA on '$($param.Computername)'" -Verbose $Job Write-LogFunctionExit } #endregion Install-LWLabCAServers #region Install-LWLabCAServers2008 function Install-LWLabCAServers2008 { [Cmdletbinding()] param ( [Parameter(Mandatory)] [hashtable]$param ) Write-LogFunctionEntry #$VerbosePreference = 'Continue' #$DebugPreference = 'Continue' #region - Parameters debug Write-Debug -Message '---------------------------------------------------------------------------------------' Write-Debug -Message 'Parameters - Entered Install-LWLabCAServers' Write-Debug -Message '---------------------------------------------------------------------------------------' if ($param.GetEnumerator().count) { foreach ($key in ($param.GetEnumerator() | Sort-Object -Property Name)) { Write-Debug -message " $($key.key.padright(27)) $($key.value)" } } else { Write-Debug -message ' No parameters specified' } Write-Debug -Message '---------------------------------------------------------------------------------------' Write-Debug -Message '' #endregion - Parameters debug #region ScriptBlock for installation $caScriptBlock = { param ($param) function Install-WebEnrollment { [CmdletBinding()] param ( [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [string]$CAConfig ) # check if web enrollment binaries are installed Import-Module ServerManager # instanciate COM object try { $EWPSetup = New-Object -ComObject CertOCM.CertSrvSetup.1 } catch { Write-Warning "Unable to load necessary interfaces. Your Windows Server operating system is not supported!" return } # initialize the object to install only web enrollment $EWPSetup.InitializeDefaults($false,$true) try { # set required information and install the role $EWPSetup.SetWebCAInformation($CAConfig) $EWPSetup.Install() } catch { $_ return } Write-Host "Successfully installed Enrollment Web Pages on local computer!" -ForegroundColor Green } Import-Module -Name ServerManager #region - Check if CA is already installed Write-Verbose -Message 'Check if ADCS-Cert-Authority is already installed' if ((Get-WindowsFeature -Name 'ADCS-Cert-Authority').Installed) { Write-Verbose -Message 'ADCS-Cert-Authority is already installed. Returning' Write-Output "A Certificate Authority is already installed on '$($param.ComputerName)'. Skipping installation." return } #endregion Write-Verbose -Message 'Create CAPolicy.inf file' #region - Create CAPolicy file $caPolicyFileName = "$Env:Windir\CAPolicy.inf" Set-Content $caPolicyFileName -Force -Value ';CAPolicy for CA' Add-Content $caPolicyFileName -Value '; Please replace sample CPS OID with your own OID' Add-Content $caPolicyFileName -Value '' Add-Content $caPolicyFileName -Value '[Version]' Add-Content $caPolicyFileName -Value "Signature=`"`$Windows NT`$`" " Add-Content $caPolicyFileName -Value '' Add-Content $caPolicyFileName -Value '[PolicyStatementExtension]' Add-Content $caPolicyFileName -Value 'Policies=LegalPolicy' Add-Content $caPolicyFileName -Value 'Critical=0' Add-Content $caPolicyFileName -Value '' Add-Content $caPolicyFileName -Value '[LegalPolicy]' Add-Content $caPolicyFileName -Value 'OID=1.3.6.1.4.1.11.21.43' Add-Content $caPolicyFileName -Value "Notice=$($param.CpsText)" Add-Content $caPolicyFileName -Value "URL=$($param.CpsUrl)" Add-Content $caPolicyFileName -Value '' Add-Content $caPolicyFileName -Value '[Certsrv_Server]' Add-Content $caPolicyFileName -Value 'ForceUTF8=true' Add-Content $caPolicyFileName -Value "RenewalKeyLength=$($param.KeyLength)" Add-Content $caPolicyFileName -Value "RenewalValidityPeriod=$($param.ValidityPeriod)" Add-Content $caPolicyFileName -Value "RenewalValidityPeriodUnits=$($param.ValidityPeriodUnits)" Add-Content $caPolicyFileName -Value "CRLPeriod=$($param.CRLPeriod)" Add-Content $caPolicyFileName -Value "CRLPeriodUnits=$($param.CRLPeriodUnits)" Add-Content $caPolicyFileName -Value "CRLDeltaPeriod=$($param.CRLDeltaPeriod)" Add-Content $caPolicyFileName -Value "CRLDeltaPeriodUnits=$($param.CRLDeltaPeriodUnits)" Add-Content $caPolicyFileName -Value 'EnableKeyCounting=0' Add-Content $caPolicyFileName -Value 'AlternateSignatureAlgorithm=0' if ($param.DoNotLoadDefaultTemplates -eq 'True') { Add-Content $caPolicyFileName -Value 'LoadDefaultTemplates=0' } if ($param.CAType -like '*root*') { Add-Content $caPolicyFileName -Value '' Add-Content $caPolicyFileName -Value '[Extensions]' Add-Content $caPolicyFileName -Value ';Remove CA Version Index' Add-Content $caPolicyFileName -Value '1.3.6.1.4.1.311.21.1=' Add-Content $caPolicyFileName -Value ';Remove CA Hash of previous CA Certificates' Add-Content $caPolicyFileName -Value '1.3.6.1.4.1.311.21.2=' Add-Content $caPolicyFileName -Value ';Remove V1 Certificate Template Information' Add-Content $caPolicyFileName -Value '1.3.6.1.4.1.311.20.2=' Add-Content $caPolicyFileName -Value ';Remove CA of V2 Certificate Template Information' Add-Content $caPolicyFileName -Value '1.3.6.1.4.1.311.21.7=' Add-Content $caPolicyFileName -Value ';Key Usage Attribute set to critical' Add-Content $caPolicyFileName -Value '2.5.29.15=AwIBBg==' Add-Content $caPolicyFileName -Value 'Critical=2.5.29.15' } if ($param.DebugPref -eq 'Continue') { $file = get-content -Path "$Env:Windir\CAPolicy.inf" Write-Debug -Message 'CApolicy.inf contents:' foreach ($line in $file) { Write-Debug -Message $line } } #endregion - Create CAPolicy file #region - Install CA $hostOSVersion = [system.version](Get-WmiObject -Class Win32_OperatingSystem).Version if ($hostOSVersion -ge [system.version]'6.2') { $InstallFeatures = 'Import-Module -Name ServerManager; Add-WindowsFeature -IncludeManagementTools -Name ADCS-Cert-Authority' } else { $InstallFeatures = 'Import-Module -Name ServerManager; Add-WindowsFeature -Name ADCS-Cert-Authority' } # OCSP not yet supported #if ($param.InstallOCSP) { $InstallFeatures += ", ADCS-Online-Cert" } if ($param.InstallWebEnrollment) { $InstallFeatures += ', ADCS-Web-Enrollment' } Write-Verbose -Message "Install roles and feature using command '$InstallFeatures'" Invoke-Expression -Command ($InstallFeatures += " -Confirm:`$false") | Out-Null if ($param.ForestAdminUserName) { Write-Verbose -Message "ForestAdminUserName=$($param.ForestAdminUserName), ForestAdminPassword=$($param.ForestAdminPassword)" Write-Verbose -Message "Adding $($param.ForestAdminUserName) to local administrators group" Write-Verbose -Message "WinNT:://$($param.ForestAdminUserName.replace('\', '/'))" $localGroup = ([ADSI]'WinNT://./Administrators,group') $localGroup.psbase.Invoke('Add', ([ADSI]"WinNT://$($param.ForestAdminUserName.replace('\', '/'))").path) $forestAdminCred = (New-Object System.Management.Automation.PSCredential($param.ForestAdminUserName, ($param.ForestAdminPassword | ConvertTo-SecureString -AsPlainText -Force))) } else { Write-Verbose -Message 'No ForestAdminUserName!' } try { $CASetup = New-Object -ComObject CertOCM.CertSrvSetup.1 } catch { Write-Verbose -Message "Unable to load necessary interfaces. Operating system is not supported for PKI." return } try { $CASetup.InitializeDefaults($true, $false) } catch { Write-Verbose -Message "Cannot initialize setup binaries!" } $CATypesByVal = @{} $CATypesByName.keys | ForEach-Object {$CATypesByVal.Add($CATypesByName[$_],$_)} $CAPRopertyByName = @{"CAType"=0 "CAKeyInfo"=1 "Interactive"=2 "ValidityPeriodUnits"=5 "ValidityPeriod"=6 "ExpirationDate"=7 "PreserveDataBase"=8 "DBDirectory"=9 "Logdirectory"=10 "ParentCAMachine"=12 "ParentCAName"=13 "RequestFile"=14 "WebCAMachine"=15 "WebCAName"=16} $CAPRopertyByVal = @{} $CAPRopertyByName.keys | ForEach-Object ` { $CAPRopertyByVal.Add($CAPRopertyByName[$_],$_) } $ValidityUnitsByName = @{"years" = 6} $ValidityUnitsByVal = @{6 = "years"} $ofs = ", " #key length and hashing algorithm verification $CAKey = $CASetup.GetCASetupProperty(1) if ($param.CryptoProviderName -ne "") { if ($CASetup.GetProviderNameList() -notcontains $param.CryptoProviderName) { # TODO add available CryptoProviderName list Write-Host "Specified CSP '$param.CryptoProviderName' is not valid!" } else { $CAKey.ProviderName = $param.CryptoProviderName } } else { $CAKey.ProviderName = "RSA#Microsoft Software Key Storage Provider" } Write-Verbose -Message "ProviderName = '$($CAKey.ProviderName)'" if ($param.KeyLength -ne 0) { if ($CASetup.GetKeyLengthList($param.CryptoProviderName).Length -eq 1) { $CAKey.Length = $CASetup.GetKeyLengthList($param.CryptoProviderName)[0] } else { if ($CASetup.GetKeyLengthList($param.CryptoProviderName) -notcontains $param.KeyLength) { Write-Host "The specified key length '$KeyLength' is not supported by the selected CryptoProviderName '$param.CryptoProviderName'" Write-Host "The following key lengths are supported by this CryptoProviderName:" foreach ($provider in ($CASetup.GetKeyLengthList($param.CryptoProviderName))) { Write-Host " $provider" } } $CAKey.Length = $param.KeyLength } } Write-Verbose -Message "KeyLength = '$($CAKey.KeyLength)'" if ($param.HashAlgorithmName -ne "") { if ($CASetup.GetHashAlgorithmList($param.CryptoProviderName) -notcontains $param.HashAlgorithmName) { Write-ScreenInfo -Message "The specified hash algorithm is not supported by the selected CryptoProviderName '$param.CryptoProviderName'" Write-ScreenInfo -Message "The following hash algorithms are supported by this CryptoProviderName:" -Type Error foreach ($algorithm in ($CASetup.GetHashAlgorithmList($param.CryptoProviderName))) { Write-ScreenInfo -Message " $algorithm" -Type Error } } $CAKey.HashAlgorithm = $param.HashAlgorithmName } $CASetup.SetCASetupProperty(1,$CAKey) Write-Verbose -Message "Hash Algorithm = '$($CAKey.HashAlgorithm)'" if ($param.CAType) { $SupportedTypes = $CASetup.GetSupportedCATypes() $CATypesByName = @{'EnterpriseRootCA'=0;'EnterpriseSubordinateCA'=1;'StandaloneRootCA'=3;'StandaloneSubordinateCA'=4} $SelectedType = $CATypesByName[$param.CAType] if ($SupportedTypes -notcontains $SelectedType) { Write-Host "Selected CA type: '$CAType' is not supported by current Windows Server installation." Write-Host "The following CA types are supported by this installation:" #foreach ($caType in ( { #Write-ScreenInfo -Message "$([int[]]$CASetup.GetSupportedCATypes() | %{$CATypesByVal[$_]}) } } } else { $CASetup.SetCASetupProperty($CAPRopertyByName.CAType,$SelectedType) } Write-Verbose -Message "CAType = '$($param.CAType)'" if ($SelectedType -eq 0 -or $SelectedType -eq 3 -and $param.ValidityPeriodUnits -ne 0) { try { $CASetup.SetCASetupProperty(6,([int]$param.ValidityPeriodUnits)) } catch { Write-Host "The specified CA certificate validity period '$($param.ValidityPeriodUnits)' is invalid." } } Write-Verbose -Message "ValidityPeriod = '$($param.ValidityPeriodUnits)'" $DN = New-Object -ComObject X509Enrollment.CX500DistinguishedName # validate X500 name format try { $DN.Encode("CN=$($param.CACommonName)",0x0) } catch { Write-Host "Specified CA name or CA name suffix is not correct X.500 Distinguished Name." } $CASetup.SetCADistinguishedName("CN=$($param.CACommonName)", $true, $true, $true) Write-Verbose -Message "CADistinguishedName = 'CN=$($param.CACommonName)'" if ($CASetup.GetCASetupProperty(0) -eq 1 -and $param.ParentCA) { [void]($param.ParentCA -match "^(.+)\\(.+)$") try { $CASetup.SetParentCAInformation($param.ParentCA) } catch { Write-Host "The specified parent CA information '$param.ParentCA' is incorrect. Make sure if parent CA information is correct (you must specify existing CA) and is supplied in a 'CAComputerName\CASanitizedName' form." } } Write-Verbose -Message "PArentCA = 'CN=$($param.CACommonName)'" if ($param.DatabaseDirectory -eq '') { $param.DatabaseDirectory = 'C:\Windows\system32\CertLog' } Write-Verbose -Message "DatabaseDirectory = '$($param.DatabaseDirectory)'" if ($param.LogDirectory -eq '') { $param.LogDirectory = 'C:\Windows\system32\CertLog' } Write-Verbose -Message "LogDirectory = '$($param.LogDirectory)'" if ($param.DatabaseDirectory -ne "" -and $param.LogDirectory -ne "") { try { $CASetup.SetDatabaseInformation($param.DatabaseDirectory,$param.LogDirectory,$null,$OverwriteExisting) } catch { Write-Verbose -Message 'Specified path to either database directory or log directory is invalid.' } } try { Write-Verbose -Message 'Installing Certification Authority' $CASetup.Install() if ($CASetup.GetCASetupProperty(0) -eq 1) { $CASName = (Get-ItemProperty HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration).Active $SetupStatus = (Get-ItemProperty HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration\$CASName).SetupStatus $RequestID = (Get-ItemProperty HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration\$CASName).RequestID } Write-Verbose -Message 'Certification Authority role is successfully installed' } catch { Write-Error $_ -ErrorAction Stop } if ($param.ForestAdminUserName) { Write-Verbose -Message "Removing $($param.ForestAdminUserName) to local administrators group" $localGroup = ([ADSI]'WinNT://./Administrators,group') $localGroup.psbase.Invoke('Remove', ([ADSI]"WinNT://$($param.ForestAdminUserName.replace('\', '/'))").path) } if ($param.InstallWebEnrollment) { Write-Verbose -Message 'InstallWebRole is True, hence setting InstallWebRole to True' $param.InstallWebRole = $true } if ($param.InstallWebRole) { Write-Verbose -Message 'Check if web role is already installed' if (!((Get-WindowsFeature -Name 'web-server').Installed)) { Write-Verbose -Message 'Web role is NOT already installed. Installing it now.' Add-WindowsFeature -Name 'Web-Server' -IncludeManagementTools #Allow "+" characters in URL for supporting delta CRLs #Set-WebConfiguration -Filter system.webServer/security/requestFiltering -PSPath 'IIS:\sites\Default Web Site' -Value @{allowDoubleEscaping=$true} } } if ($param.InstallWebEnrollment) { Write-Verbose -Message 'Installing Web Enrollment service' Install-WebEnrollment "$($param.ComputerName)\$($param.CACommonName)" } #endregion - Install CA #region - Configure IIS virtual directories if ($param.UseHTTPAia) { #New-WebVirtualDirectory -Site 'Default Web Site' -Name Aia -PhysicalPath 'C:\Windows\System32\CertSrv\CertEnroll' | Out-Null #New-WebVirtualDirectory -Site 'Default Web Site' -Name Cdp -PhysicalPath 'C:\Windows\System32\CertSrv\CertEnroll' | Out-Null } #endregion - Configure IIS virtual directories #region - Configure CA function Invoke-CustomExpression { param ($Command) Invoke-Expression -Command $command Write-Verbose -Message $command } #Declare configuration NC if ($param.CAType -like 'Enterprise*') { $lDAPname = '' foreach ($part in ($param.DomainName.split('.'))) { $lDAPname += ",DC=$part" } Invoke-CustomExpression -Command "certutil.exe -setreg ""CA\DSConfigDN"" ""CN=Configuration$lDAPname""" } #Apply the required CDP Extension URLs $command = "certutil.exe -setreg CA\CRLPublicationURLs ""1:$($Env:WinDir)\system32\CertSrv\CertEnroll\%3%8%9.crl" if ($param.UseLDAPCRL) { $command += '\n11:ldap:///CN=%7%8,CN=%2,CN=CDP,CN=Public Key Services,CN=Services,%6%10' } if ($param.UseHTTPCRL) { $command += "\n2:$($param.CDPHTTPURL01)/%3%8%9.crl" } if ($param.AIAHTTPURL01UploadLocation) { $command += "\n1:$($param.AIAHTTPURL01UploadLocation)/%3%8%9.crl" } $command += '"' Invoke-CustomExpression -Command $command #Apply the required AIA Extension URLs $command = "certutil.exe -setreg CA\CACertPublicationURLs ""1:$($Env:WinDir)\system3\CertSrv\CertEnroll\%1_%3%4.crt" if ($param.UseLDAPAia) { $command += '\n3:ldap:///CN=%7,CN=AIA,CN=Public Key Services,CN=Services,%6%11' } if ($param.UseHTTPAia) { $command += "\n2:$($param.AIAHTTPURL01)/%1_%3%4.crt" } if ($param.AIAHTTPURL01UploadLocation) { $command += "\n1:$($param.AIAHTTPURL01UploadLocation)/%3%8%9.crl" } $command += '"' Invoke-CustomExpression -Command $command #Define default maximum certificate lifetime for issued certificates Invoke-CustomExpression -Command "certutil.exe -setreg ca\ValidityPeriodUnits $($param.CertsValidityPeriodUnits)" Invoke-CustomExpression -Command "certutil.exe -setreg ca\ValidityPeriod ""$($param.CertsValidityPeriod)""" #Define CRL Publication Intervals Invoke-CustomExpression -Command "certutil.exe -setreg CA\CRLPeriodUnits $($param.CRLPeriodUnits)" Invoke-CustomExpression -Command "certutil.exe -setreg CA\CRLPeriod ""$($param.CRLPeriod)""" #Define CRL Overlap Invoke-CustomExpression -Command "certutil.exe -setreg CA\CRLOverlapUnits $($param.CRLOverlapUnits)" Invoke-CustomExpression -Command "certutil.exe -setreg CA\CRLOverlapPeriod ""$($param.CRLOverlapPeriod)""" #Define Delta CRL Invoke-CustomExpression -Command "certutil.exe -setreg CA\CRLDeltaUnits $($param.CRLDeltaPeriodUnits)" Invoke-CustomExpression -Command "certutil.exe -setreg CA\CRLDeltaPeriod ""$($param.CRLDeltaPeriod)""" #Enable Auditing Logging Invoke-CustomExpression -Command 'certutil.exe -setreg CA\Auditfilter 0x7F' #Enable UTF-8 Encoding Invoke-CustomExpression -Command 'certutil.exe -setreg ca\forceteletex +0x20' if ($param.CAType -like '*root*') { #Disable Discrete Signatures in Subordinate Certificates (WinXP KB968730) Invoke-CustomExpression -Command 'certutil.exe -setreg CA\csp\AlternateSignatureAlgorithm 0' #Force digital signature removal in KU for cert issuance (see also kb888180) Invoke-CustomExpression -Command 'certutil.exe -setreg policy\EditFlags -EDITF_ADDOLDKEYUSAGE' #Enable SAN Invoke-CustomExpression -Command 'certutil.exe -setreg policy\EditFlags +EDITF_ATTRIBUTESUBJECTALTNAME2' #Configure policy module to automatically issue certificates when requested Invoke-CustomExpression -Command 'certutil.exe -setreg ca\PolicyModules\CertificateAuthority_MicrosoftDefault.Policy\RequestDisposition 1' } #If CA is Root CA and Sub CAs are present, disable (do not publish) templates (except SubCA template) if ($param.DoNotLoadDefaultTemplates) { Invoke-CustomExpression -Command 'certutil.exe -SetCATemplates +SubCA' } #endregion - Configure CA #region - Restart of CA if ((Get-Service -Name 'CertSvc').Status -eq 'Running') { Write-Verbose -Message 'Stopping ADCS Service' $totalretries = 5 $retries = 0 do { Stop-Service -Name 'CertSvc' -ErrorAction SilentlyContinue if ((Get-Service -Name 'CertSvc').Status -ne 'Stopped') { $retries++ Start-Sleep -Seconds 1 } } until (((Get-Service -Name 'CertSvc').Status -eq 'Stopped') -or ($retries -ge $totalretries)) if ((Get-Service -Name 'CertSvc').Status -eq 'Stopped') { Write-Verbose -Message 'ADCS service is now stopped' } else { Write-Error -Message 'Could not stop ADCS Service after several retries' return } } Write-Verbose -Message 'Starting ADCS Service now' $totalretries = 5 $retries = 0 do { Start-Service -Name 'CertSvc' -ErrorAction SilentlyContinue if ((Get-Service -Name 'CertSvc').Status -ne 'Running') { $retries++ Start-Sleep -Seconds 1 } } until (((Get-Service -Name 'CertSvc').Status -eq 'Running') -or ($retries -ge $totalretries)) if ((Get-Service -Name 'CertSvc').Status -eq 'Running') { Write-Verbose -Message 'ADCS service is now started' } else { Write-Error -Message 'Could not start ADCS Service after several retries' return } #endregion - Restart of CA Write-Verbose -Message 'Waiting for admin interface to be ready' $totalretries = 10 $retries = 0 do { $result = Invoke-Expression -Command "certutil.exe -pingadmin .\$($param.CACommonName)" if (!($result | Where-Object { $_ -like '*interface is alive*' })) { $retries++ Write-Verbose -Message "Admin interface not ready. Check $retries of $totalretries" if ($retries -lt $totalretries) { Start-Sleep -Seconds 10 } } } until (($result | Where-Object { $_ -like '*interface is alive*' }) -or ($retries -ge $totalretries)) if ($result | Where-Object { $_ -like '*interface is alive*' }) { Write-Verbose -Message 'Admin interface is now ready' } else { Write-Error -Message 'Admin interface was not ready after several retries' return } #region - Issue of CRL Start-Sleep -Seconds 2 Invoke-Expression -Command 'certutil.exe -crl' | Out-Null $totalretries = 12 $retries = 0 do { Start-Sleep -Seconds 5 $retries++ } until ((Get-ChildItem "$env:systemroot\system32\CertSrv\CertEnroll\*.crl") -or ($retries -ge $totalretries)) #endregion - Issue of CRL if (($param.CAType -like 'Enterprise*') -and ($param.DoNotLoadDefaultTemplates)) { Invoke-Expression 'certutil.exe -SetCATemplates +SubCA' } } #endregion Write-Verbose -Message "Performing installation of $($param.CAType) on '$($param.ComputerName)'" $cred = (New-Object System.Management.Automation.PSCredential($param.UserName, ($param.Password | ConvertTo-SecureString -AsPlainText -Force))) $caSession = New-LabPSSession -ComputerName $param.ComputerName $Job = Invoke-Command -Session $caSession -Scriptblock $caScriptBlock -ArgumentList $param -AsJob -JobName "Install CA on '$($param.Computername)'" -Verbose $Job Write-LogFunctionExit } #endregion Install-LWLabCAServers2008 |