DSCResources/MSFT_xCertReq/MSFT_xCertReq.psm1
function Get-TargetResource { [CmdletBinding()] [OutputType([System.Collections.Hashtable])] param ( [parameter(Mandatory = $true)] [System.String] $Subject, [parameter(Mandatory = $true)] [System.String] $CAServerFQDN, [parameter(Mandatory = $true)] [System.String] $CARootName ) $Cert = Get-Childitem Cert:\LocalMachine\My | ? {$_.Subject -eq "CN=$Subject" -and $_.Issuer.split(',')[0] -eq "CN=$CARootName"} # If multiple certs have the same subject and were issued by the CA, return the newest $Cert = $Cert | Sort-Object NotBefore -Descending | Select -first 1 $returnValue = @{ Subject = if ($Cert){[System.String]$Cert.Subject}; CAServerFQDN = if ($Cert){'Issued By: '+[System.String]$Cert.Issuer}; CARootName = if ($Cert){[System.String]$Cert.Issuer.split(',')[0].replace('CN=','')} } $returnValue } # Get-TargetResource 'test.domain.com' -CAServerFQDN 'dc01.test.net' -CARootName 'test-dc01-ca' function Set-TargetResource { [CmdletBinding()] param ( [parameter(Mandatory = $true)] [System.String] $Subject, [parameter(Mandatory = $true)] [System.String] $CAServerFQDN, [parameter(Mandatory = $true)] [System.String] $CARootName, [System.Management.Automation.PSCredential] $Credential, [System.Boolean] $AutoRenew ) # If the Subject does not contain a full X500 path, construct just the CN if (($Subject.split('=').count) -eq 1) { [System.String]$Subject = "CN=$Subject" } # If we should look for renewals, check for existing certs if ($AutoRenew) { $Cert = Get-Childitem Cert:\LocalMachine\My | ? {$_.Subject -eq $Subject -and $_.Issuer.split(',')[0] -eq "CN=$CARootName" -and $_.NotAfter -lt (get-date).AddDays(-30)} # If multiple certs have the same subject and were issued by the CA and are 30 days from expiration, return the newest $Thumprint = $Cert | Sort-Object NotBefore -Descending | Select -first 1 | foreach {$_.Thumbprint} } # Information that will be used in the INF file to generate the certificate request # In future versions, select variables from the list below could be moved to parameters! [System.String]$Subject = "`"$Subject`"" [System.String]$KeySpec = '1' [System.String]$KeyLength = '1024' [System.String]$Exportable = 'TRUE' [System.String]$MachineKeySet = 'TRUE' [System.String]$SMIME = 'False' [System.String]$PrivateKeyArchive = 'FALSE' [System.String]$UserProtected = 'FALSE' [System.String]$UseExistingKeySet = 'FALSE' [System.String]$ProviderName = '"Microsoft RSA SChannel Cryptographic Provider"' [System.String]$ProviderType = '12' [System.String]$RequestType = 'CMC' [System.String]$KeyUsage = '0xa0' [System.String]$OID = '1.3.6.1.5.5.7.3.1' [System.String]$CertificateTemplate = 'WebServer' # A unique identifier for temporary files that will be used when interacting with the command line utility [system.guid]$GUID = [system.guid]::NewGuid().guid [System.String]$INF = "$env:Temp\$GUID.inf" [System.String]$REQ = "$env:Temp\$GUID.req" [System.String]$CER = "$env:Temp\$GUID.cer" [System.String]$RSP = "$env:Temp\$GUID.rsp" # The certificate authority, accessible on the local area network [System.String]$CA = "$CAServerFQDN\$CARootName" # Create INF file $requestDetails = @" [NewRequest] Subject = $Subject KeySpec = $KeySpec KeyLength = $KeyLength Exportable = $Exportable MachineKeySet = $MachineKeySet SMIME = $SMIME PrivateKeyArchive = $PrivateKeyArchive UserProtected = $UserProtected UseExistingKeySet = $UseExistingKeySet ProviderName = $ProviderName ProviderType = $ProviderType RequestType = $RequestType KeyUsage = $KeyUsage [RequestAttributes] CertificateTemplate=$CertificateTemplate [EnhancedKeyUsageExtension] OID=$OID "@ if ($Thumbprint) {$requestDetails += "RenewalCert = $Thumbprint"} $requestDetails| out-file $INF # NEW: Create a new request as directed by PolicyFileIn $createRequest = C:\windows\system32\certreq.exe -new -q $INF $REQ # SUBMIT: Submit a request to a Certification Authority. # DSC runs in the context of LocalSystem, which uses the Computer account in Active Directory to authenticate to network resources # The Credential paramter with xPDT is used to impersonate a user making the request if (test-path $REQ) { if ($Credential) { Import-Module $PSScriptRoot\..\..\xPDT.psm1 $Process = StartWin32Process -Path 'C:\windows\system32\certreq.exe' -Arguments "-submit -q -config $CA $REQ $CER" -Credential $Credential Write-Verbose $Process WaitForWin32ProcessEnd -Path 'C:\windows\system32\certreq.exe' -Arguments "-submit -q -config $CA $REQ $CER" -Credential $Credential } else { $submitRequest = C:\windows\system32\certreq.exe -submit -q -config $CA $REQ $CER write-verbose $submitRequest[2]} } # Accept request if (test-path $CER) { write-verbose 'Accepting certificate' $acceptRequest = C:\windows\system32\certreq.exe -accept -machine -q $CER } write-verbose 'Cleaning up files' #foreach ($file in @($INF,$REQ,$CER,$RSP)) {if (test-path $file) {Remove-Item $file -force}} # Syntax: https://technet.microsoft.com/en-us/library/cc736326.aspx # Reference: https://support2.microsoft.com/default.aspx?scid=kb;EN-US;321051 } # Set-TargetResource 'test.domain.com' -CAServerFQDN 'dc01.test.net' -CARootName 'test-dc01-ca' -credential (get-credential) function Test-TargetResource { [CmdletBinding()] [OutputType([System.Boolean])] param ( [parameter(Mandatory = $true)] [System.String] $Subject, [parameter(Mandatory = $true)] [System.String] $CAServerFQDN, [parameter(Mandatory = $true)] [System.String] $CARootName, [System.Management.Automation.PSCredential] $Credential, [System.Boolean] $AutoRenew ) # If the Subject does not contain a full X500 path, construct just the CN if (($Subject.split('=').count) -eq 1) { [System.String]$Subject = "CN=$Subject" } $Cert = Get-Childitem Cert:\LocalMachine\My | ? {$_.Subject -eq $Subject -and $_.Issuer.split(',')[0] -eq "CN=$CARootName"} # If multiple certs have the same subject and were issued by the CA, return the newest $Cert = $Cert | Sort-Object NotBefore -Descending | Select -first 1 Write-Verbose "Checking Certificates" if ($AutoRenew) { if ($Cert.NotAfter -gt (get-date).AddDays(-30)) { [boolean]$true } else { [boolean]$false Write-Verbose "No valid certificate found with subject $Subject from CA $CARootName, or certificate is about to expire" } } else { if ($Cert.NotAfter -gt (get-date)) { [boolean]$true } else { [boolean]$false Write-Verbose "No valid certificate found with subject $Subject from CA $CARootName" } } } # Test-TargetResource 'test.domain.com' -CAServerFQDN 'dc01.test.net' -CARootName 'test-dc01-ca' Export-ModuleMember -Function *-TargetResource |