jb1981

GenLeCertForNS v0.8.1

Apr 11th, 2017
276
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. <#
  2. .SYNOPSIS
  3.     Create a new or update an existing Let's Encrypt certificate for one or more domains and add it to a store then update the SSL bindings for a NetScaler
  4. .DESCRIPTION
  5.     The script will use ACMESharp to create a new or update an existing certificate for one or more domains. If generated successfully the script will add the certificate to the NetScaler and update the SSL binding for a web site. This script is for use with a Citrix NetScaler (v11.x and up). The script will validate the dns records provided. For example, the domain(s) listed must be configured with the same IP Address that is configured (via NAT) to a Content Switch.
  6. .PARAMETER Help
  7.     Display the detailed information about this script
  8. .PARAMETER CleanNS
  9.     Cleanup the NetScaler configuration made within this script, for when somewhere it gone wrong
  10. .PARAMETER NSManagementURL
  11.     Management URL, used to connect to the NetScaler
  12. .PARAMETER NSUserName
  13.     NetScaler username with enough access to configure it
  14. .PARAMETER NSPassword
  15.     NetScaler username password
  16. .PARAMETER NSCredential
  17.     Use a PSCredential object instead of a username or password. Use "Get-Credential" to generate a credential object
  18.     C:\PS> $Credential = Get-Credential
  19. .PARAMETER NSCsVipName
  20.     Name of the HTTP NetScaler Content Switch used for the domain validation
  21.     .PARAMETER NSCsVipBinding
  22.     NetScaler Content Switch binding used for the validation
  23. .PARAMETER NSSvcName
  24.     NetScaler Load Balance service name
  25. .PARAMETER NSSvcDestination
  26.     IP Address used for the NetScaler Service (leave default 1.2.3.4, only change when already used
  27. .PARAMETER NSLbName
  28.     NetScaler Load Balance VIP name
  29. .PARAMETER NSRspName
  30.     NetScaler Responder Policy name
  31. .PARAMETER NSRsaName
  32.     NetScaler Responder Action name
  33. .PARAMETER NSCspName
  34.     NetScaler Content Switch Policy name
  35. .PARAMETER NSCertNameToUpdate
  36.     NetScaler SSL Certkey name currently in use, that needs to be renewd
  37. .PARAMETER CertDir
  38.     Directory where to store the certificates
  39. .PARAMETER PfxPassword
  40.     Password for the PFX certificate, generated at the end
  41. .PARAMETER EmailAddress
  42.     The email address used to request the certificates and receive a notification when the certificates (almost) expires
  43. .PARAMETER cn
  44.     (Common Name) The Primary (first) dns record for the certificaten
  45. .PARAMETER san
  46.     (Subject Alternate Name) every following domain listed in this certificate. sepatated via an comma , and between quotes "".
  47.     E.g.: "sts.domain.com","www.domain.com","vpn.domain.com"
  48. .PARAMETER Production
  49.     Use the production Let's encryt server
  50. .PARAMETER DisableIPCheck
  51.     If you want to skip the IP Address verification, specify this parameter
  52. .PARAMETER CleanVault
  53.     Force initialization of the vault before use
  54. .PARAMETER SaveNSConfig
  55.     Save the NetScaler config after all the changes.
  56. .PARAMETER ns10x
  57.     When using v10x, some nitro functions will not work propperly, run the script with this parameter.
  58. .EXAMPLE
  59.     .\GenLeCertForNS.ps1 -CN "domain.com" -EmailAddress "[email protected]" -SAN "sts.domain.com","www.domain.com","vpn.domain.com" -PfxPassword "P@ssw0rd" -CertDir "C:\Certificates" -NSManagementURL "http://192.168.100.1" -NSCsVipName "cs_domain.com_http" -NSPassword "nsroot" -NSUserName "P@ssw0rd" -NSCertNameToUpdate "san_domain_com" -Production -CleanVault -Verbose
  60.     Generate a (Production)certificate for hostname "domain.com" with alternate names : "sts.domain.com, www.domain.com, vpn.domain.com". Using the emailaddress "[email protected]". At the end storing the certificates  in "C:\Certificates" and uploading them to the NetScaler. Also Cleaning the vault on the NetScaler the content Switch "cs_domain.com_http" will be used to validate the certificates.
  61. .EXAMPLE
  62.     .\GenLeCertForNS.ps1 -CleanNS -NSManagementURL "http://192.168.100.1" -NSCsVipName "cs_domain.com_http" -NSPassword "nsroot" -NSUserName "P@ssw0rd" -Verbose
  63.     Cleaning left over configuration from this schript when something went wrong during a previous attempt to generate new certificates and generating Verbose output.
  64. .NOTES
  65.     File Name : GenLeCertForNS.ps1
  66.     Version   : v0.8.1
  67.     Author    : John Billekens
  68.     Requires  : PowerShell v3 and up
  69.                 NetScaler 11.x and up
  70.                 Run As Administrator
  71.                 ACMESharp (can be installed via this script)
  72. .LINK
  73.     https://blog.j81.nl
  74. #>
  75.  
  76. [cmdletbinding(DefaultParametersetName="ConfigNetScaler")]
  77. param(
  78.         [Parameter(ParameterSetName="Help",Mandatory=$false)]
  79.         [alias("h")]
  80.         [switch]$Help,
  81.        
  82.         [Parameter(ParameterSetName="CleanNetScaler",Mandatory=$true)]
  83.         [switch]$CleanNS,
  84.        
  85.         [Parameter(ParameterSetName="ConfigNetScaler",Mandatory=$true)]
  86.         [Parameter(ParameterSetName="CleanNetScaler",Mandatory=$true)]
  87.         [ValidateNotNullOrEmpty()]
  88.         [alias("URL")]
  89.         [string]$NSManagementURL,
  90.        
  91.         [Parameter(ParameterSetName="ConfigNetScaler",Mandatory=$false)]
  92.         [Parameter(ParameterSetName="CleanNetScaler",Mandatory=$false)]
  93.         [alias("User", "Username")]
  94.         [string]$NSUserName,
  95.        
  96.         [Parameter(ParameterSetName="ConfigNetScaler",Mandatory=$false)]
  97.         [Parameter(ParameterSetName="CleanNetScaler",Mandatory=$false)]
  98.         [alias("Password")]
  99.         [string]$NSPassword,
  100.  
  101.         [Parameter(ParameterSetName="ConfigNetScaler",Mandatory=$false)]
  102.         [Parameter(ParameterSetName="CleanNetScaler",Mandatory=$false)]
  103.         [ValidateScript({
  104.             if ($_ -is [System.Management.Automation.PSCredential]) {
  105.                 $true
  106.             } elseif ($_ -is [string]) {
  107.                 $Script:Credential=Get-Credential -Credential $_
  108.                 $true
  109.             } else {
  110.                 Write-Error "You passed an unexpected object type for the credential (-NSCredential)"
  111.             }
  112.         })][alias("Credential")]
  113.         [object]$NSCredential,
  114.        
  115.         [Parameter(ParameterSetName="ConfigNetScaler",Mandatory=$true)]
  116.         [Parameter(ParameterSetName="CleanNetScaler",Mandatory=$true)]
  117.         [ValidateNotNullOrEmpty()]
  118.         [string]$NSCsVipName,
  119.        
  120.         [Parameter(ParameterSetName="ConfigNetScaler",Mandatory=$false)]
  121.         [Parameter(ParameterSetName="CleanNetScaler",Mandatory=$false)]
  122.         [string]$NSCsVipBinding = 11,
  123.        
  124.         [Parameter(ParameterSetName="ConfigNetScaler",Mandatory=$false)]
  125.         [Parameter(ParameterSetName="CleanNetScaler",Mandatory=$false)]
  126.         [string]$NSSvcName = "svc_letsencrypt_cert_dummy",
  127.        
  128.         [Parameter(ParameterSetName="ConfigNetScaler",Mandatory=$false)]
  129.         [Parameter(ParameterSetName="CleanNetScaler",Mandatory=$false)]
  130.         [string]$NSSvcDestination = "1.2.3.4",
  131.        
  132.         [Parameter(ParameterSetName="ConfigNetScaler",Mandatory=$false)]
  133.         [Parameter(ParameterSetName="CleanNetScaler",Mandatory=$false)]
  134.         [string]$NSLbName = "lb_letsencrypt_cert",
  135.        
  136.         [Parameter(ParameterSetName="ConfigNetScaler",Mandatory=$false)]
  137.         [Parameter(ParameterSetName="CleanNetScaler",Mandatory=$false)]
  138.         [string]$NSRspName = "rsp_letsencrypt",
  139.        
  140.         [Parameter(ParameterSetName="ConfigNetScaler",Mandatory=$false)]
  141.         [Parameter(ParameterSetName="CleanNetScaler",Mandatory=$false)]
  142.         [string]$NSRsaName = "rsa_letsencrypt",
  143.        
  144.         [Parameter(ParameterSetName="ConfigNetScaler",Mandatory=$false)]
  145.         [Parameter(ParameterSetName="CleanNetScaler",Mandatory=$false)]
  146.         [string]$NSCspName = "csp_NSCertCsp",
  147.        
  148.         [Parameter(ParameterSetName="ConfigNetScaler",Mandatory=$false)]
  149.         [string]$NSCertNameToUpdate,
  150.        
  151.         [Parameter(ParameterSetName="ConfigNetScaler",Mandatory=$true)]
  152.         [ValidateNotNullOrEmpty()]
  153.         [string]$CertDir,
  154.        
  155.         [Parameter(ParameterSetName="ConfigNetScaler",Mandatory=$false)]
  156.         [string]$PfxPassword = $null,
  157.        
  158.         [Parameter(ParameterSetName="ConfigNetScaler",Mandatory=$true)]
  159.         [ValidateNotNullOrEmpty()]
  160.         [string]$CN,
  161.        
  162.         [Parameter(ParameterSetName="ConfigNetScaler",Mandatory=$true)]
  163.         [string]$EmailAddress,
  164.        
  165.         [Parameter(ParameterSetName="ConfigNetScaler",Mandatory=$false)]
  166.         [string[]]$SAN=@(),
  167.        
  168.         [Parameter(ParameterSetName="ConfigNetScaler",Mandatory=$false)]
  169.         [switch]$Production,
  170.        
  171.         [Parameter(ParameterSetName="ConfigNetScaler",Mandatory=$false)]
  172.         [switch]$DisableIPCheck,
  173.  
  174.         [Parameter(ParameterSetName="ConfigNetScaler",Mandatory=$false)]
  175.         [switch]$CleanVault,
  176.        
  177.         [Parameter(ParameterSetName="ConfigNetScaler",Mandatory=$false)]
  178.         [Parameter(ParameterSetName="CleanNetScaler",Mandatory=$false)]
  179.         [switch]$SaveNSConfig,
  180.        
  181.         [Parameter(ParameterSetName="ConfigNetScaler",Mandatory=$false)]
  182.         [Parameter(ParameterSetName="CleanNetScaler",Mandatory=$false)]
  183.         [switch]$ns10x
  184. )
  185.  
  186. #requires -version 3.0
  187. #requires -runasadministrator
  188.  
  189. #region Functions
  190.  
  191. function InvokeNSRestApi {
  192.     [CmdletBinding()]
  193.     param (
  194.         [Parameter(Mandatory=$true)]
  195.         [PSObject]$Session,
  196.  
  197.         [Parameter(Mandatory=$true)]
  198.         [ValidateSet('DELETE', 'GET', 'POST', 'PUT')]
  199.         [string]$Method,
  200.  
  201.         [Parameter(Mandatory=$true)]
  202.         [string]$Type,
  203.  
  204.         [string]$Resource,
  205.  
  206.         [string]$Action,
  207.  
  208.         [hashtable]$Arguments = @{},
  209.  
  210.         [switch]$Stat = $false,
  211.  
  212.         [ValidateScript({$Method -eq 'GET'})]
  213.         [hashtable]$Filters = @{},
  214.  
  215.         [ValidateScript({$Method -ne 'GET'})]
  216.         [hashtable]$Payload = @{},
  217.  
  218.         [switch]$GetWarning = $false,
  219.  
  220.         [ValidateSet('EXIT', 'CONTINUE', 'ROLLBACK')]
  221.         [string]$OnErrorAction = 'EXIT'
  222.     )
  223.     # https://github.com/devblackops/NetScaler
  224.     if ([string]::IsNullOrEmpty($($Session.ManagementURL))) {
  225.         throw "ERROR. Probably not logged into the NetScaler"
  226.     }
  227.     if ($Stat) {
  228.         $uri = "$($Session.ManagementURL)/nitro/v1/stat/$Type"
  229.     } else {
  230.         $uri = "$($Session.ManagementURL)/nitro/v1/config/$Type"
  231.     }
  232.     if (-not ([string]::IsNullOrEmpty($Resource))) {
  233.         $uri += "/$Resource"
  234.     }
  235.     if ($Method -ne 'GET') {
  236.         if (-not ([string]::IsNullOrEmpty($Action))) {
  237.             $uri += "?action=$Action"
  238.         }
  239.  
  240.         if ($Arguments.Count -gt 0) {
  241.             $queryPresent = $true
  242.             if ($uri -like '*?action*') {
  243.                 $uri += '&args='
  244.             } else {
  245.                 $uri += '?args='
  246.             }
  247.             $argsList = @()
  248.             foreach ($arg in $Arguments.GetEnumerator()) {
  249.                 $argsList += "$($arg.Name):$([System.Uri]::EscapeDataString($arg.Value))"
  250.             }
  251.             $uri += $argsList -join ','
  252.         }
  253.     } else {
  254.         $queryPresent = $false
  255.         if ($Arguments.Count -gt 0) {
  256.             $queryPresent = $true
  257.             $uri += '?args='
  258.             $argsList = @()
  259.             foreach ($arg in $Arguments.GetEnumerator()) {
  260.                 $argsList += "$($arg.Name):$([System.Uri]::EscapeDataString($arg.Value))"
  261.             }
  262.             $uri += $argsList -join ','
  263.         }
  264.         if ($Filters.Count -gt 0) {
  265.             $uri += if ($queryPresent) { '&filter=' } else { '?filter=' }
  266.             $filterList = @()
  267.             foreach ($filter in $Filters.GetEnumerator()) {
  268.                 $filterList += "$($filter.Name):$([System.Uri]::EscapeDataString($filter.Value))"
  269.             }
  270.             $uri += $filterList -join ','
  271.         }
  272.     }
  273.     Write-Verbose -Message "URI: $uri"
  274.  
  275.     $jsonPayload = $null
  276.     if ($Method -ne 'GET') {
  277.         $warning = if ($GetWarning) { 'YES' } else { 'NO' }
  278.         $hashtablePayload = @{}
  279.         $hashtablePayload.'params' = @{'warning' = $warning; 'onerror' = $OnErrorAction; <#"action"=$Action#>}
  280.         $hashtablePayload.$Type = $Payload
  281.         $jsonPayload = ConvertTo-Json -InputObject $hashtablePayload -Depth 100
  282.         Write-Verbose -Message "JSON Payload:`n$jsonPayload"
  283.     }
  284.  
  285.     $response = $null
  286.     $restError = $null
  287.     try {
  288.         $restError = @()
  289.         $restParams = @{
  290.             Uri = $uri
  291.             ContentType = 'application/json'
  292.             Method = $Method
  293.             WebSession = $Session.WebSession
  294.             ErrorVariable = 'restError'
  295.             Verbose = $false
  296.         }
  297.  
  298.         if ($Method -ne 'GET') {
  299.             $restParams.Add('Body', $jsonPayload)
  300.         }
  301.  
  302.         $response = Invoke-RestMethod @restParams
  303.  
  304.         if ($response) {
  305.             if ($response.severity -eq 'ERROR') {
  306.                 throw "Error. See response: `n$($response | Format-List -Property * | Out-String)"
  307.             } else {
  308.                 Write-Verbose -Message "Response:`n$(ConvertTo-Json -InputObject $response | Out-String)"
  309.                 if ($Method -eq "GET") { return $response }
  310.             }
  311.         }
  312.     }
  313.     catch [Exception] {
  314.         if ($Type -eq 'reboot' -and $restError[0].Message -eq 'The underlying connection was closed: The connection was closed unexpectedly.') {
  315.             Write-Verbose -Message 'Connection closed due to reboot'
  316.         } else {
  317.             throw $_
  318.         }
  319.     }
  320. }
  321.  
  322. function Connect-NetScaler {
  323.     [cmdletbinding()]
  324.     param(
  325.         [parameter(Mandatory)]
  326.         [string]$ManagementURL,
  327.  
  328.         [parameter(Mandatory)]
  329.         [pscredential]$Credential = (Get-Credential -Message 'NetScaler credential'),
  330.  
  331.         [int]$Timeout = 3600,
  332.  
  333.         [switch]$PassThru
  334.     )
  335.     # https://github.com/devblackops/NetScaler
  336.     Write-Verbose -Message "Connecting to $ManagementURL..."
  337.     try {
  338.         if ($script:ns10x) {
  339.             $login = @{
  340.                 login = @{
  341.                     username = $Credential.UserName;
  342.                     password = $Credential.GetNetworkCredential().Password
  343.                 }
  344.             }
  345.         } else {
  346.             $login = @{
  347.                 login = @{
  348.                     username = $Credential.UserName;
  349.                     password = $Credential.GetNetworkCredential().Password
  350.                     timeout = $Timeout
  351.                 }
  352.             }
  353.         }
  354.         $loginJson = ConvertTo-Json -InputObject $login
  355.         Write-Verbose "JSON Data:`n$($loginJson | Out-String)"
  356.         $saveSession = @{}
  357.         $params = @{
  358.             Uri = "$ManagementURL/nitro/v1/config/login"
  359.             Method = 'POST'
  360.             Body = $loginJson
  361.             SessionVariable = 'saveSession'
  362.             ContentType = 'application/json'
  363.             ErrorVariable = 'restError'
  364.             Verbose = $false
  365.         }
  366.         $response = Invoke-RestMethod @params
  367.  
  368.         if ($response.severity -eq 'ERROR') {
  369.             throw "Error. See response: `n$($response | Format-List -Property * | Out-String)"
  370.         } else {
  371.             Write-Verbose -Message "Response:`n$(ConvertTo-Json -InputObject $response | Out-String)"
  372.         }
  373.     } catch [Exception] {
  374.         throw $_
  375.     }
  376.  
  377.     $session = [PSObject]@{
  378.         ManagementURL=[string]$ManagementURL;
  379.         WebSession=[Microsoft.PowerShell.Commands.WebRequestSession]$saveSession;
  380.     }
  381.  
  382.     $Script:NSSession = $session
  383.    
  384.     if($PassThru){
  385.             return $session
  386.     }
  387. }
  388.  
  389. #endregion Functions
  390.  
  391. #region Help
  392.  
  393. if($Help){
  394.     Write-Verbose "Generating help for `"$ScriptFilename`""
  395.     Get-Help "$ScriptFilename" -Full
  396.     Exit(0)
  397. }
  398.  
  399. #endregion Help
  400.  
  401. #region Script variables
  402.  
  403. if ($ns10x){
  404.     Write-Verbose "ns10x parameter userd, some options are now disabled."
  405. }
  406. Write-Verbose "Generating Session GUID"
  407. [string]$SessionGUID = [guid]::NewGuid()
  408. Write-Verbose "Setting session DATE/TIME variable"
  409. [string]$SessionDateTime = (Get-Date).ToString("yyyyMMddHHmm")
  410. if (-not([string]::IsNullOrWhiteSpace($NSCredential))) {
  411.     Write-Verbose "Using NSCredential"
  412. } elseif ((-not([string]::IsNullOrWhiteSpace($NSUserName))) -and (-not([string]::IsNullOrWhiteSpace($NSPassword)))){
  413.     Write-Verbose "Using NSUsername / NSPassword"
  414.     [pscredential]$NSCredential = new-object -typename System.Management.Automation.PSCredential -argumentlist $NSUserName, $(ConvertTo-SecureString -String $NSPassword -AsPlainText -Force)
  415. } else {
  416.     Write-Verbose "No valid username/password or credential specified. Enter a username and password, e.g. `"nsroot`""
  417.     [pscredential]$NSCredential = Get-Credential -Message "NetScaler username and password:"
  418. }
  419. Write-Verbose "Starting new session ($SessionGUID)"
  420. if(-not ([string]::IsNullOrWhiteSpace($SAN))){
  421.     [string[]]$SAN = @($SAN.Split(","))
  422. }
  423.  
  424. #endregion Script variables
  425.  
  426. #region Load Module
  427. if (-not ($CleanNS)) {
  428.     Write-Verbose "Load ACMESharp Modules"
  429.     if (-not(Get-Module ACMESharp)){
  430.         try {
  431.             $ACMEVersions = (get-Module -Name ACMESharp -ListAvailable).Version
  432.             $ACMEUpdateRequired = $false
  433.             ForEach ($ACMEVersion in $ACMEVersions) {
  434.                 if (($ACMEVersion.Minor -eq 8) -and ($ACMEVersion.Build -eq 1) -and (-not $ACMEUpdateRequired)) {
  435.                     Write-Verbose "v0.8.1 of ACMESharp is installed, continuing"
  436.                 } else {
  437.                     Write-Verbose "v0.8.1 of ACMESharp is NOT installed, update/downgrade required"
  438.                     $ACMEUpdateRequired = $true
  439.                 }
  440.             }
  441.             if ($ACMEUpdateRequired) {
  442.                 Write-Verbose "Trying to update the ACMESharp modules"
  443.                 Install-Module -Name ACMESharp -Scope AllUsers -RequiredVersion 0.8.1 -Force -ErrorAction SilentlyContinue
  444.             }
  445.             Write-Verbose "Try loading module ACMESharp"
  446.             Import-Module ACMESharp -ErrorAction Stop
  447.         } catch [System.IO.FileNotFoundException] {
  448.             Write-Verbose "Checking for PackageManagement"
  449.             if ([string]::IsNullOrWhiteSpace($(Get-Module -ListAvailable -Name PackageManagement))) {
  450.                 Write-Warning "PackageManagement is not available please install this first or manually install ACMESharp"
  451.                 Write-Warning "Visit `"https://www.microsoft.com/en-us/download/details.aspx?id=51451`" to download Package Management"
  452.                 Write-Warning "ACMESharp: https://github.com/ebekker/ACMESharp"
  453.                 Start "https://www.microsoft.com/en-us/download/details.aspx?id=49186"
  454.                 Exit (1)
  455.             } else {
  456.                 try {
  457.                     if (-not ((Get-PackageProvider -Name NuGet -ErrorAction SilentlyContinue).Version -ge [System.Version]"2.8.5.208")) {
  458.                         Write-Verbose "Installing Nuget"
  459.                         Get-PackageProvider -Name NuGet -Force -ErrorAction SilentlyContinue | Out-Null
  460.                     }
  461.                     $installationPolicy = (Get-PSRepository -Name PSGallery).InstallationPolicy
  462.                     if (-not ($installationPolicy.ToLower() -eq "trusted")){
  463.                         Write-Verbose "Defining PSGallery PSRepository as trusted"
  464.                         Set-PSRepository -Name "PSGallery" -InstallationPolicy Trusted
  465.                     }
  466.                     Write-Verbose "Installing ACMESharp"
  467.                     try {
  468.                         Install-Module -Name ACMESharp -Scope AllUsers -RequiredVersion 0.8.1 -Force -AllowClobber
  469.                     } catch {
  470.                         Write-Verbose "Installing ACMESharp again but without the -AllowClobber option"
  471.                         Install-Module -Name ACMESharp -Scope AllUsers -RequiredVersion 0.8.1 -Force
  472.                     }
  473.                     if (-not ((Get-PSRepository -Name PSGallery).InstallationPolicy -eq $installationPolicy)){
  474.                         Write-Verbose "Returning the PSGallery PSRepository InstallationPolicy to previous value"
  475.                         Set-PSRepository -Name "PSGallery" -InstallationPolicy $installationPolicy | Out-Null
  476.                     }
  477.                     Write-Verbose "Try loading module ACMESharp"
  478.                     Import-Module ACMESharp -ErrorAction Stop
  479.                 } catch {
  480.                     Write-Verbose "Error Details: $($_.Exception.Message)"
  481.                     Write-Error "Error while loading and/or installing module"
  482.                     Exit (1)
  483.                 }
  484.             }
  485.        
  486.         }
  487.     }
  488. }
  489. #endregion Load Module
  490.  
  491. #region Vault
  492. if (-not ($CleanNS)) {
  493.     if ($Production) {
  494.         $VaultName = ":sys"
  495.         $BaseService = "LetsEncrypt"
  496.         Write-Verbose "Using the vault `"$VaultName`" for production certificates"
  497.     } else {
  498.         $VaultName = ":user"   
  499.         $BaseService = "LetsEncrypt-STAGING"
  500.         Write-Verbose "Using the vault `"$VaultName`" for test/staging purposes"
  501.     }
  502.     try {
  503.         Write-Verbose "Get ACMEVault `"$VaultName`""
  504.         $VaultData = Get-ACMEVault -VaultProfile $VaultName
  505.     } catch {
  506.         Write-Verbose "`"$VaultName`" Vault not available, initialize"
  507.         $CleanVault = $true
  508.     }
  509.     if ($CleanVault) {
  510.         Write-Verbose "Initializing Vault"
  511.         Initialize-ACMEVault -VaultProfile $VaultName -Force
  512.         Write-Verbose "Finished initializing"
  513.         $VaultData = Get-ACMEVault -VaultProfile $VaultName
  514.     }
  515.     Write-Verbose "Configure vault `"$VaultName`" for `"$BaseService`""
  516.     Set-ACMEVault -VaultProfile $VaultName -BaseService $BaseService
  517. }
  518. #endregion Vault
  519.  
  520. #region Registration
  521. if (-not ($CleanNS)) {
  522.     try {
  523.         Write-Verbose "Retreive existing Registration"
  524.         $Registration = Get-ACMERegistration -VaultProfile $VaultName
  525.         if ($Registration.Contacts -contains "mailto:$EmailAddress"){
  526.             Write-Verbose "Existing registration found, no changes necessary"
  527.         } else {
  528.             Write-Verbose "Current registration `"$($Registration.Contacts)`" is not equal to `"$EmailAddress`", setting new registration"
  529.             $Registration = New-ACMERegistration -VaultProfile $VaultName -Contacts mailto:$EmailAddress -AcceptTos
  530.         }
  531.     } catch {
  532.         Write-Verbose "Setting new registration to `"$EmailAddress`""
  533.         $Registration = New-ACMERegistration -VaultProfile $VaultName -Contacts mailto:$EmailAddress -AcceptTos
  534.     }
  535. }
  536. #endregion Registration
  537.  
  538. #region DNS
  539.  
  540. #region Primary DNS
  541. if (-not ($CleanNS)) {
  542.     Write-Verbose "Validating DNS record(s)"
  543.     $DNSObjects = @()
  544.    
  545.     Write-Verbose "Checking `"$CN`""
  546.     try {
  547.         if ($DisableIPCheck){
  548.             Write-Warning "Skipping IP check, validation might fail"
  549.             $PrimaryIP = "NoIPCheck"
  550.         } else {
  551.             $PublicDnsServer = "208.67.222.222"
  552.             Write-Verbose "Using public DNS server (OpenDNS, 208.67.222.222) to verify dns records"
  553.             Write-Verbose "Trying to get IP Address"
  554.             $PrimaryIP = (Resolve-DnsName -Server $PublicDnsServer -Name $CN -DnsOnly -Type A -ErrorAction SilentlyContinue).IPAddress
  555.             if ([string]::IsNullOrWhiteSpace($PrimaryIP)) {
  556.                 throw "ERROR: No valid entry found for DNSName:`"$CN`""
  557.             }
  558.             if ($PrimaryIP -is [system.array]){
  559.                 Write-Warning "More than one ip address found`n$($PrimaryIP | Format-Table | Out-String)"
  560.                 $PrimaryIP = $PrimaryIP[0]
  561.                 Write-Warning "using the first one`"$PrimaryIP`""
  562.             }
  563.         }
  564.     } catch {
  565.         Write-Verbose "Error Details: $($_.Exception.Message)"
  566.         Write-Verbose "Error while retreiving IP Address"
  567.         throw "Error while retreiving IP Address, does not exists?"
  568.     }
  569.    
  570.    
  571.     try {
  572.         Write-Verbose "Start validation process for `"$CN`""
  573.         $IdentifierAlias = "$($CN)-$($SessionGUID)"
  574.         $Identifier = New-ACMEIdentifier -Dns $CN -Alias $IdentifierAlias -VaultProfile $VaultName
  575.     } catch {
  576.         try {
  577.             Write-Verbose "Posibly it already exists, retreiving data"
  578.             $Identifier = Get-ACMEIdentifier -IdentifierRef $CN -VaultProfile $VaultName
  579.         } catch {
  580.             Write-Verbose "Record is invalid"
  581.             $Identifier = [PSCustomObject]@{
  582.                 Status = "invalid"
  583.                 Expires = $null
  584.             }
  585.         }
  586.     }
  587.     try {
  588.         Write-Verbose "Extracting data, checking validation"
  589.         $response = Invoke-RestMethod -Uri $Identifier.Uri -Method Get
  590.         $result = $response  | Select-Object status,expires
  591.         $Identifier = [PSCustomObject]@{
  592.             Status = $result.status
  593.             Expires = $result.expires
  594.         }
  595.     }catch{
  596.         Write-Verbose "Someting went wrong with the validation: $($result | Format-Table | Out-String)"
  597.     }
  598.     Write-Verbose "Checking if current validation is still valid"
  599.     if (($Identifier.status -eq "valid") -and ($([datetime]$Identifier.Expires - $(Get-Date)).TotalDays -gt 0.3)) {
  600.         Write-Verbose "`"$CN`" is valid"
  601.         $Validation = $true
  602.     } else {
  603.         Write-Verbose "`"$CN`" is NOT valid"
  604.         $Validation = $false
  605.     }
  606.     Write-Verbose "Validation response: $($result | Format-Table | Out-String)"
  607.     Write-Verbose "Storing values"
  608.     $DNSObjects += [PSCustomObject]@{
  609.         DNSName = $CN
  610.         IPAddress = $PrimaryIP
  611.         Status = $(if ([string]::IsNullOrWhiteSpace($PrimaryIP)) {$false} else {$true})
  612.         Match = $null
  613.         SAN = $false
  614.         DNSValid = $Validation
  615.         Alias = $IdentifierAlias
  616.     }
  617. }
  618. Write-Verbose "$($DNSObjects | Format-Table | Out-String)"
  619.  
  620. #endregion Primary DNS
  621.  
  622. #region SAN
  623. if (-not ($CleanNS)) {
  624.     Write-Verbose "Checking if SAN entries are available"
  625.     if ([string]::IsNullOrWhiteSpace($SAN)) {
  626.         Write-Verbose "No SAN entries found"
  627.     } elseif (($($SAN.Count) -eq 1) -and ($SAN[0] -eq $CN)) {
  628.         Write-Verbose "Skipping SAN, CN:`"$CN`" is equal to SAN:`"$($SAN[0])`""
  629.     } else {
  630.         Write-Verbose "$($SAN.Count) found, checking each one"
  631.         foreach ($DNSRecord in $SAN) {
  632.             try {
  633.                 if ($DisableIPCheck) {
  634.                     Write-Verbose "Skipping IP check"
  635.                     $SANIP = "NoIPCheck"
  636.                 } else {
  637.                     Write-Verbose "Start basic IP Check for `"$DNSRecord`", trying to get IP Address"
  638.                     $SANIP = (Resolve-DnsName -Server $PublicDnsServer -Name $DNSRecord -DnsOnly -Type A -ErrorAction SilentlyContinue).IPAddress
  639.                     if ($SANIP -is [system.array]){
  640.                         Write-Warning "More than one ip address found`n$($SANIP | Format-Table | Out-String)"
  641.                         $SANIP = $SANIP[0]
  642.                         Write-Warning "using the first one`"$SANIP`""
  643.                     }
  644.                     Write-Verbose "Finished, Result: $SANIP"
  645.                 }
  646.                
  647.             } catch {
  648.                 Write-Verbose "Error while retreiving IP Address"
  649.                 Write-Host -ForeGroundColor Red "Error while retreiving IP Address, does not exists?"
  650.                 $SANIP = $null
  651.             }
  652.             if ([string]::IsNullOrWhiteSpace($SANIP)) {
  653.                 Write-Verbose "No valid entry found for DNSName:`"$DNSRecord`""
  654.                 $SANMatch = $false
  655.                 $SANStatus = $false
  656.             } else {
  657.                 Write-Verbose "Valid entry found"
  658.                 $SANStatus = $true
  659.                 if ($DisableIPCheck) {
  660.                     Write-Verbose "IP address checking was disabled"
  661.                     $SANMatch = $true
  662.                 } else {
  663.                     Write-Verbose "All IP Adressess must match, checking"
  664.                     if ($SANIP -match $($DNSObjects[0].IPAddress)) {
  665.                         Write-Verbose "`"$SANIP ($DNSRecord)`" matches to `"$($DNSObjects[0].IPAddress) ($($DNSObjects[0].DNSName))`""
  666.                         $SANMatch = $true
  667.                     } else {
  668.                         Write-Verbose "`"$SANIP`" ($DNSRecord) NOT matches to `"$($DNSObjects[0].IPAddress)`" ($($DNSObjects[0].DNSName))"
  669.                         $SANMatch = $false
  670.                     }
  671.                 }
  672.             }
  673.             try {
  674.                 Write-Verbose "Start validation process for `"$DNSRecord`""
  675.                 $IdentifierAlias = "$($DNSRecord)-$($SessionGUID)"
  676.                 $Identifier = New-ACMEIdentifier -Dns $DNSRecord -Alias $IdentifierAlias -VaultProfile $VaultName
  677.             } catch {
  678.                 try {
  679.                     Write-Verbose "Posibly it already exists, retreiving data"
  680.                     $Identifier = Get-ACMEIdentifier -IdentifierRef $DNSRecord -VaultProfile $VaultName
  681.                 } catch {
  682.                     Write-Verbose "Record is invalid"
  683.                     $Identifier = [PSCustomObject]@{
  684.                         Status = "invalid"
  685.                         Expires = $null
  686.                     }
  687.                 }
  688.             }
  689.             try {
  690.                 if ($Identifier.Uri) {
  691.                     Write-Verbose "Extracting data, checking validation"
  692.                     $response = Invoke-RestMethod -Uri $Identifier.Uri -Method Get
  693.                     $result = $response  | Select-Object status,expires
  694.                     $Identifier = [PSCustomObject]@{
  695.                         Status = $result.status
  696.                         Expires = $result.expires
  697.                     }
  698.                 } else {
  699.                     Write-Verbose "Nothing to extract, probably a new request"
  700.                 }
  701.             }catch{
  702.                 Write-Verbose "Someting went wrong with the validation: $($result | Format-Table | Out-String)"
  703.             }
  704.             Write-Verbose "Checking if current validation is still valid"
  705.             if (($Identifier.status -eq "valid") -and ($([datetime]$Identifier.Expires - $(Get-Date)).TotalDays -gt 0.3)) {
  706.                 Write-Verbose "`"$DNSRecord`" is valid"
  707.                 $Validation = $true
  708.             } else {
  709.                 Write-Verbose "`"$DNSRecord`" is NOT valid"
  710.                 $Validation = $false
  711.             }
  712.             Write-Verbose "Validation response: $($result | Format-Table | Out-String)"
  713.             $DNSObjects += [PSCustomObject]@{
  714.                 DNSName = $DNSRecord
  715.                 IPAddress = $SANIP
  716.                 Status = $SANStatus
  717.                 Match = $SANMatch
  718.                 SAN = $true
  719.                 DNSValid = $Validation
  720.                 Alias = $IdentifierAlias
  721.             }
  722.         }
  723.     }
  724. }
  725. Write-Verbose "$($DNSObjects | Format-Table | Out-String)"
  726.  
  727. #endregion SAN
  728.  
  729. if (-not ($CleanNS)) {
  730.     Write-Verbose "Checking for invalid DNS Records"
  731.     $InvalidDNS = $DNSObjects | Where-Object {$_.Status -eq $false}
  732.     if ($InvalidDNS) {
  733.         Write-Verbose "$($InvalidDNS | Select-Object DNSName,Status | Format-Table | Out-String)"
  734.         $DNSObjects[0] | Select-Object DNSName,IPAddress | Format-Table | Out-String | Foreach {Write-Host -ForeGroundColor Green "$_"}
  735.         $InvalidDNS | Select-Object DNSName,IPAddress | Format-Table | Out-String | Foreach {Write-Host -ForeGroundColor Red "$_"}
  736.         throw "ERROR, invalid (not registered?) DNS Record(s) found:`r`n"
  737.     } else {
  738.         Write-Verbose "None found, continuing"
  739.     }
  740.     Write-Verbose "Checking non matching DNS Records"
  741.     $DNSNoMatch = $DNSObjects | Where-Object {$_.Match -eq $false}
  742.     if ($DNSNoMatch -and (-not $DisableIPCheck)) {
  743.         Write-Verbose "$($DNSNoMatch | Select-Object DNSName,Match | Format-Table | Out-String)"
  744.         $DNSObjects[0] | Select-Object DNSName,IPAddress | Format-Table | Out-String | Foreach {Write-Host -ForeGroundColor Green "$_"}
  745.         $DNSNoMatch | Select-Object DNSName,IPAddress | Format-Table | Out-String | Foreach {Write-Host -ForeGroundColor Red "$_"}
  746.         throw "ERROR: Non-matching records found, must match to `"$($DNSObjects[0].DNSName)`" ($($DNSObjects[0].IPAddress))"
  747.     } else {
  748.         Write-Verbose "All IP Adressess match"
  749.     }
  750. }
  751. #region ACME DNS Verification
  752.  
  753. #region NetScaler pre dns
  754.    
  755. if (-not ($CleanNS)) {
  756.     Write-Verbose "Checking if validation is required"
  757.     $DNSValidationRequired = $DNSObjects | Where-Object {$_.DNSValid -eq $false}
  758.     try {
  759.         Write-Verbose "Login to NetScaler and save session to global variable"
  760.         $NSSession = Connect-NetScaler -ManagementURL $NSManagementURL -Credential $NSCredential -PassThru
  761.         if ($DNSValidationRequired) {
  762.             Write-Verbose "$($DNSValidationRequired | Select-Object DNSName,DNSValid | Format-Table | Out-String)"
  763.             Write-Verbose "$($DNSValidationRequired.Count) items need validation"
  764.             Write-Verbose "Enable required NetScaler Features, Load Balancer, Responder and Content Switch"
  765.             $payload = @{"feature"="LB RESPONDER CS APPFLOW"}
  766.             $response = InvokeNSRestApi -Session $NSSession -Method POST -Type nsfeature -Payload $payload -Action enable
  767.             try {
  768.                 Write-Verbose "Verifying Content Switch"
  769.                 $response = InvokeNSRestApi -Session $NSSession -Method GET -Type csvserver -Resource $NSCsVipName
  770.             } catch {
  771.                 $ExcepMessage = $_.Exception.Message
  772.                 Write-Verbose "Error Details: $ExcepMessage"
  773.                 throw "Could not find/read out the content switch `"$NSCsVipName`" not available?"
  774.             } finally {
  775.                 if ($ExcepMessage -like "*(404) Not Found*") {
  776.                     Write-Host -ForeGroundColor Red "`nThe Content Switch `"$NSCsVipName`" does NOT exists!"
  777.                     Exit (1)
  778.                 } elseif ($ExcepMessage -like "*The remote server returned an error*") {
  779.                     Write-Host -ForeGroundColor Red "`nUnknown error found while checking the Content Switch: `"$NSCsVipName`""
  780.                     Write-Host -ForeGroundColor Red "Error message: `"$ExcepMessage`""
  781.                     Exit (1)
  782.                 } elseif (($response.errorcode -eq "0") -and (-not ($response.csvserver.servicetype -eq "HTTP"))) {
  783.                     Write-Host -ForeGroundColor Red "`nThe Content Switch is $($response.csvserver.servicetype) and NOT HTTP"
  784.                     Write-Host -ForeGroundColor Red "Please use a HTTP (Port 80) Content Switch this is required for the validation. Exiting now`n"
  785.                     Exit (1)
  786.                 }
  787.             }
  788.             try {
  789.                 Write-Verbose "Configuring NetScaler: Check if Load Balancer Service exists"
  790.                 $response = InvokeNSRestApi -Session $NSSession -Method GET -Type service -Resource $NSSvcName
  791.                 Write-Verbose "Yep it exists, continuing"
  792.             } catch {
  793.                 Write-Verbose "It does not exist, continuing"
  794.                 Write-Verbose "Configuring NetScaler: Create Load Balance Service `"$NSSvcName`""
  795.                 $payload = @{"name"="$NSSvcName";"ip"="$NSSvcDestination";"servicetype"="HTTP";"port"="80";"healthmonitor"="NO";}
  796.                 $response = InvokeNSRestApi -Session $NSSession -Method POST -Type service -Payload $payload -Action add
  797.             }
  798.             try {
  799.                 Write-Verbose "Configuring NetScaler: Check if Load Balancer exists"
  800.                 $response = InvokeNSRestApi -Session $NSSession -Method GET -Type lbvserver -Resource $NSLbName
  801.                 Write-Verbose "Yep it exists, continuing"
  802.             } catch {
  803.                 Write-Verbose "Nope, continuing"
  804.                 Write-Verbose "Configuring NetScaler: Create Load Balance Vip `"$NSLbName`""
  805.                 $payload = @{"name"="$NSLbName";"servicetype"="HTTP";"ipv46"="0.0.0.0";"Port"="0";}
  806.                 $response = InvokeNSRestApi -Session $NSSession -Method POST -Type lbvserver -Payload $payload -Action add
  807.             } finally {
  808.                 Write-Verbose "Configuring NetScaler: Bind Service `"$NSSvcName`" to Load Balance Vip `"$NSLbName`""
  809.                 $payload = @{"name"="$NSLbName";"servicename"="$NSSvcName";}
  810.                 $response = InvokeNSRestApi -Session $NSSession -Method PUT -Type lbvserver_service_binding -Payload $payload
  811.             }
  812.             try {
  813.                 Write-Verbose "Configuring NetScaler: Check if Responder Policy exists"
  814.                 $response = InvokeNSRestApi -Session $NSSession -Method GET -Type responderpolicy -Resource $NSRspName
  815.                 try {
  816.                     Write-Verbose "Yep it exists, continuing"
  817.                     Write-Verbose "Configuring NetScaler: Change Responder Policy to default values"
  818.                     $payload = @{"name"="$NSRspName";"action"="rsa_letsencrypt";"rule"='HTTP.REQ.URL.CONTAINS("well-known/acme-challenge/XXXXXX")';}
  819.                     $response = InvokeNSRestApi -Session $NSSession -Method POST -Type responderpolicy -Payload $payload -Action set
  820.                 } catch {
  821.                     throw "Something went wrong with reconfiguring the existing policy `"$NSRspName`", exiting now..."
  822.                 }  
  823.             } catch {
  824.                 $payload = @{"name"="$NSRsaName";"type"="respondwith";"target"='"HTTP/1.0 200 OK" +"\r\n\r\n" + "XXXX"';}
  825.                 $response = InvokeNSRestApi -Session $NSSession -Method POST -Type responderaction -Payload $payload -Action add
  826.                 $payload = @{"name"="$NSRspName";"action"="$NSRsaName";"rule"='HTTP.REQ.URL.CONTAINS("well-known/acme-challenge/XXXX")';}
  827.                 $response = InvokeNSRestApi -Session $NSSession -Method POST -Type responderpolicy -Payload $payload -Action add
  828.             } finally {
  829.                 $payload = @{"name"="$NSLbName";"policyname"="$NSRspName";"priority"=100;}
  830.                 $response = InvokeNSRestApi -Session $NSSession -Method PUT -Type lbvserver_responderpolicy_binding -Payload $payload -Resource $NSLbName
  831.             }
  832.             try {
  833.                 Write-Verbose "Configuring NetScaler: Check if Content Switch Policy exists"
  834.                 $response = InvokeNSRestApi -Session $NSSession -Method GET -Type cspolicy -Resource $NSCspName
  835.                 Write-Verbose "It does, continuing"
  836.                 if (-not($response.cspolicy.rule -eq "HTTP.REQ.URL.CONTAINS(`"well-known/acme-challenge/`")")) {
  837.                     $payload = @{"policyname"="$NSCspName";"rule"="HTTP.REQ.URL.CONTAINS(`"well-known/acme-challenge/`")";}
  838.                     $response = InvokeNSRestApi -Session $NSSession -Method PUT -Type cspolicy -Payload $payload
  839.                 }
  840.             } catch {
  841.                 Write-Verbose "Configuring NetScaler: Create Content Switch Policy"
  842.                 $payload = @{"policyname"="$NSCspName";"rule"='HTTP.REQ.URL.CONTAINS("well-known/acme-challenge/")';}
  843.                 $response = InvokeNSRestApi -Session $NSSession -Method POST -Type cspolicy -Payload $payload -Action add
  844.                
  845.                
  846.             }
  847.             Write-Verbose "Configuring NetScaler: Bind Load Balancer `"$NSLbName`" to Content Switch `"$NSCsVipName`" with prio: $NSCsVipBinding"
  848.             $payload = @{"name"="$NSCsVipName";"policyname"="$NSCspName";"priority"="$NSCsVipBinding";"targetlbvserver"="$NSLbName";"gotopriorityexpression"="END";}
  849.             $response = InvokeNSRestApi -Session $NSSession -Method PUT -Type csvserver_cspolicy_binding -Payload $payload
  850.             Write-Verbose "Finished configuring the NetScaler"
  851.         }
  852.     } catch {
  853.         Write-Verbose "Error Details: $($_.Exception.Message)"
  854.         throw "ERROR: Could not configure the NetScaler, exiting now"
  855.     }
  856. }
  857.  
  858. #endregion NetScaler pre dns
  859.  
  860. #region DNS Check
  861.  
  862. if (-not ($CleanNS)) {
  863.     Write-Verbose "Check if DNS Records need to be validated"
  864.     foreach ($DNSObject in $DNSObjects) {
  865.         Write-Verbose "Checking validation for `"$($DNSObject.DNSName)`""
  866.         if ($DNSObject.DNSValid){
  867.             Write-Verbose "Still valid"
  868.         } else {
  869.             Write-Verbose "New validation required, Start verifying"
  870.             try {
  871.                 try {
  872.                     $Challenge = ((Complete-ACMEChallenge $($DNSObject.Alias) -ChallengeType http-01 -Handler manual -VaultProfile $VaultName).Challenges | Where-Object { $_.Type -eq "http-01" }).Challenge
  873.                 } catch {
  874.                     $Challenge = ((Complete-ACMEChallenge $($DNSObject.DNSName) -ChallengeType http-01 -Handler manual -VaultProfile $VaultName).Challenges | Where-Object { $_.Type -eq "http-01" }).Challenge
  875.                 }
  876.                 Write-Verbose "Configuring NetScaler: Change Responder Policy `"$NSRspName`" to: `"HTTP.REQ.URL.CONTAINS(`"$($Challenge.FilePath)`")`""
  877.                 $payload = @{"name"="$NSRspName";"action"="$NSRsaName";"rule"="HTTP.REQ.URL.CONTAINS(`"$($Challenge.FilePath)`")";}
  878.                 $response = InvokeNSRestApi -Session $NSSession -Method POST -Type responderpolicy -Payload $payload -Action set
  879.                
  880.                 Write-Verbose "Configuring NetScaler: Change Responder Action `"$NSRsaName`" to return "
  881.                 Write-Verbose "`"HTTP/1.0 200 OK\r\n\r\n$($Challenge.FileContent)`""
  882.                 $payload = @{"name"="$NSRsaName";"target"="`"HTTP/1.0 200 OK\r\n\r\n$($Challenge.FileContent)`"";}
  883.                 $response = InvokeNSRestApi -Session $NSSession -Method POST -Type responderaction -Payload $payload -Action set
  884.                
  885.                 Write-Verbose "Wait 1 second"
  886.                 Start-Sleep -Seconds 1
  887.                 Write-Verbose "Start Submitting Challenge"
  888.                 try {
  889.                     $SubmittedChallenge = Submit-ACMEChallenge $($DNSObject.Alias) -ChallengeType http-01 -VaultProfile $VaultName
  890.                 } catch {
  891.                     $SubmittedChallenge = Submit-ACMEChallenge $($DNSObject.DNSName) -ChallengeType http-01 -VaultProfile $VaultName
  892.                 }
  893.                 Write-Verbose "Retreiving validation status"
  894.                 try {
  895.                     $UpdateIdentifier = (Update-ACMEIdentifier $($DNSObject.Alias) -ChallengeType http-01 -VaultProfile $VaultName -Alias $($DNSObject.DNSName)).Challenges | Where-Object {$_.Type -eq "http-01"}
  896.                 } catch {
  897.                     $UpdateIdentifier = (Update-ACMEIdentifier $($DNSObject.DNSName) -ChallengeType http-01 -VaultProfile $VaultName -Alias $($DNSObject.DNSName)).Challenges | Where-Object {$_.Type -eq "http-01"}
  898.                 }
  899.                 $i = 0
  900.                 while(-NOT ($UpdateIdentifier.Status.ToLower() -eq "valid")) {
  901.                     $i++
  902.                     Write-Verbose "($($i.ToString())) $($DNSObject.DNSName) is not (yet) validated, Wait 2 second"
  903.                     Start-Sleep -Seconds 2
  904.                     Write-Verbose "Retreiving validation status"
  905.                     try {
  906.                         $UpdateIdentifier = (Update-ACMEIdentifier $($DNSObject.Alias) -ChallengeType http-01 -VaultProfile $VaultName -Alias $($DNSObject.DNSName)).Challenges | Where-Object {$_.Type -eq "http-01"}
  907.                     } catch {
  908.                         $UpdateIdentifier = (Update-ACMEIdentifier $($DNSObject.DNSName) -ChallengeType http-01 -VaultProfile $VaultName -Alias $($DNSObject.DNSName)).Challenges | Where-Object {$_.Type -eq "http-01"}
  909.                     }
  910.                     if (($i -ge 60) -or ($UpdateIdentifier.Status.ToLower() -eq "invalid")) {break}
  911.                 }
  912.                 switch ($UpdateIdentifier.Status.ToLower()) {
  913.                     "pending" {
  914.                         throw "ERROR. It took to long for the validation ($($DNSObject.DNSName)) to complete, exiting now."
  915.                     }
  916.                     "invalid" {
  917.                         throw "ERROR. Validation for `"$($DNSObject.DNSName)`" is invalid! Exiting now."
  918.                     }
  919.                     "valid" {
  920.                         Write-Host -ForeGroundColor Green "Verification for `"$($DNSObject.DNSName)`" was valid, continuing"
  921.                     }
  922.                     default {
  923.                         throw "ERROR. Unexpected status for `"$($DNSObject.DNSName)`" is `"$($UpdateIdentifier.Status)`", exiting now."
  924.                     }
  925.                 }
  926.             } catch {
  927.                 Write-Verbose "Error Details: $($_.Exception.Message)"
  928.                 throw "Error while verifying `"$($DNSObject.DNSName)`", exiting now"
  929.             }
  930.         }
  931.     }
  932. }
  933.  
  934. #endregion DNS Check
  935.  
  936. #region NetScaler post DNS
  937.  
  938. if (($DNSValidationRequired) -or ($CleanNS)) {
  939.     Write-Verbose "Login to NetScaler and save session to global variable"
  940.     Connect-NetScaler -ManagementURL $NSManagementURL -Credential $NSCredential
  941.     try {
  942.         Write-Verbose "Checking if a binding exists for `"$NSCspName`""
  943.         $Filters = @{"policyname"="$NSCspName"}
  944.         $response = InvokeNSRestApi -Session $NSSession -Method GET -Type csvserver_cspolicy_binding -Resource "$NSCsVipName" -Filters $Filters
  945.         if ($response.csvserver_cspolicy_binding.policyname -eq $NSCspName) {
  946.             Write-Verbose "Removing Content Switch Loadbalance Binding"
  947.             $Arguments = @{"name"="$NSCsVipName";"policyname"="$NSCspName";"priority"="$NSCsVipBinding";}
  948.             $response = InvokeNSRestApi -Session $NSSession -Method DELETE -Type csvserver_cspolicy_binding -Arguments $Arguments
  949.         } else {
  950.             Write-Verbose "No binding found"
  951.         }
  952.     } catch {
  953.         Write-Verbose "Error Details: $($_.Exception.Message)"
  954.         Write-Warning "Not able to remove the Content Switch Loadbalance Binding"
  955.     }
  956.     try {
  957.         Write-Verbose "Checking if Content Switch Policy `"$NSCspName`" exists"
  958.         try {
  959.             $response = InvokeNSRestApi -Session $NSSession -Method GET -Type cspolicy -Resource "$NSCspName"
  960.         } catch{}
  961.         if ($response.cspolicy.policyname -eq $NSCspName) {
  962.             Write-Verbose "Removing Content Switch Policy"
  963.             $response = InvokeNSRestApi -Session $NSSession -Method DELETE -Type cspolicy -Resource "$NSCspName"
  964.         } else {
  965.             Write-Verbose "Content Switch Policy not found"
  966.         }
  967.     } catch {
  968.         Write-Verbose "Error Details: $($_.Exception.Message)"
  969.         Write-Warning "Not able to remove the Content Switch Policy"
  970.     }
  971.     try {
  972.         Write-Verbose "Checking if Load Balance vServer `"$NSLbName`" exists"
  973.         try {
  974.             $response = InvokeNSRestApi -Session $NSSession -Method GET -Type lbvserver -Resource "$NSLbName"
  975.         } catch{}
  976.         if ($response.lbvserver.name -eq $NSLbName) {
  977.             Write-Verbose "Removing the Load Balance vServer"
  978.             $response = InvokeNSRestApi -Session $NSSession -Method DELETE -Type lbvserver -Resource "$NSLbName"
  979.         } else {
  980.             Write-Verbose "Load Balance vServer not found"
  981.         }
  982.     } catch {
  983.         Write-Verbose "Error Details: $($_.Exception.Message)"
  984.         Write-Warning "Not able to remove the Load Balance vserver"
  985.     }
  986.     try {
  987.         Write-Verbose "Checking if Service `"$NSSvcName`" exists"
  988.         try {
  989.             $response = InvokeNSRestApi -Session $NSSession -Method GET -Type service -Resource "$NSSvcName"
  990.         } catch{}
  991.         if ($response.service.name -eq $NSSvcName) {
  992.             Write-Verbose "Removing Service `"$NSSvcName`""
  993.             $response = InvokeNSRestApi -Session $NSSession -Method DELETE -Type service -Resource "$NSSvcName"
  994.         } else {
  995.             Write-Verbose "Service not found"
  996.         }
  997.     } catch {
  998.         Write-Verbose "Error Details: $($_.Exception.Message)"
  999.         Write-Warning "Not able to remove the Service"
  1000.     }
  1001.     try {
  1002.         Write-Verbose "Checking if server `"$NSSvcDestination`" exists"
  1003.         try {
  1004.             $response = InvokeNSRestApi -Session $NSSession -Method GET -Type server -Resource "$NSSvcDestination"
  1005.         } catch{}
  1006.         if ($response.server.name -eq $NSSvcDestination) {
  1007.             Write-Verbose "Removing Server `"$NSSvcDestination`""
  1008.             $response = InvokeNSRestApi -Session $NSSession -Method DELETE -Type server -Resource "$NSSvcDestination"
  1009.         } else {
  1010.             Write-Verbose "Server not found"
  1011.         }
  1012.     } catch {
  1013.         Write-Verbose "Error Details: $($_.Exception.Message)"
  1014.         Write-Warning "Not able to remove the Server"
  1015.     }
  1016.     try {
  1017.         Write-Verbose "Checking if Responder Policy `"$NSRspName`" exists"
  1018.         try {
  1019.             $response = InvokeNSRestApi -Session $NSSession -Method GET -Type responderpolicy -Resource "$NSRspName"
  1020.         } catch{}
  1021.         if ($response.responderpolicy.name -eq $NSRspName) {
  1022.             Write-Verbose "Removing Responder Policy `"$NSRspName`""
  1023.             $response = InvokeNSRestApi -Session $NSSession -Method DELETE -Type responderpolicy -Resource "$NSRspName"
  1024.         } else {
  1025.             Write-Verbose "Responder Policy not found"
  1026.         }
  1027.     } catch {
  1028.         Write-Verbose "Error Details: $($_.Exception.Message)"
  1029.         Write-Warning "Not able to remove the Responder Policy"
  1030.     }
  1031.     try {
  1032.         Write-Verbose "Checking if Responder Action `"$NSRsaName`" exists"
  1033.         try {
  1034.             $response = InvokeNSRestApi -Session $NSSession -Method GET -Type responderaction -Resource "$NSRsaName"
  1035.         } catch{}
  1036.         if ($response.responderaction.name -eq $NSRsaName) {
  1037.             Write-Verbose "Removing Responder Action `"$NSRsaName`""
  1038.             $response = InvokeNSRestApi -Session $NSSession -Method DELETE -Type responderaction -Resource $NSRsaName
  1039.         } else {
  1040.             Write-Verbose "Responder Action not found"
  1041.         }
  1042.     } catch {
  1043.         Write-Verbose "Error Details: $($_.Exception.Message)"
  1044.         Write-Warning "Not able to remove the Responder Action"
  1045.     }
  1046. }  
  1047.  
  1048. #endregion NetScaler Post DNS
  1049.  
  1050. #endregion ACME DNS Verification
  1051.  
  1052. #endregion DNS
  1053.  
  1054. #region Certificates
  1055.    
  1056. if (-not ($CleanNS)) {
  1057.     $SANs = $DNSObjects | Where-Object {$_.SAN -eq $true}
  1058.     try {
  1059.         if ($SANs) {
  1060.             Write-Verbose "Get certificate with SANs (ID: $SessionGUID)"
  1061.             Write-Verbose "Domain:`n$($DNSObjects[0] | Select-Object DNSName,Alias | Format-Table | Out-String)"
  1062.             Write-Verbose "Subject Alternative Names:`n$(@($SANs) | Select-Object DNSName,Alias | Format-Table | Out-String)"
  1063.             $NewCertificate = New-ACMECertificate $DNSObjects[0].Alias `
  1064.                 -AlternativeIdentifierRefs @($SANs.Alias) `
  1065.                 -Alias $DNSObjects[0].Alias `
  1066.                 -Generate `
  1067.                 -VaultProfile $VaultName | Out-Null
  1068.         } else {
  1069.             Write-Verbose "Get single DNS Name certificate (ID: SessionGUID)"
  1070.             Write-Verbose "Domain:`r`n$($($DNSObjects[0].DNSName) | fl * | Out-String)"
  1071.             $NewCertificate = New-ACMECertificate $DNSObjects[0].Alias `
  1072.                 -Alias $DNSObjects[0].Alias `
  1073.                 -Generate `
  1074.                 -VaultProfile $VaultName | Out-Null
  1075.         }
  1076.         Write-Verbose "Submit Certificate request"
  1077.         Submit-ACMECertificate $DNSObjects[0].Alias -VaultProfile $VaultName | Out-Null
  1078.     } catch {
  1079.         throw "ERROR. Certificate completion failed, details: $($_.Exception.Message | Out-String)"
  1080.     }
  1081.     $i = 0
  1082.     while (-not (Update-ACMECertificate $DNSObjects[0].Alias -VaultProfile $VaultName | select IssuerSerialNumber)) {
  1083.         if ($i -ge 120) {
  1084.             throw "Error: Retreiving certificate failed, took to long to complete"
  1085.         }
  1086.         Write-Host "Waiting for certificate to come available..."
  1087.         Start-Sleep -seconds 2
  1088.     }
  1089.    
  1090.     $CertificateDirectory = Join-Path -Path $CertDir -ChildPath "$($SessionDateTime)-$($SessionGUID)"
  1091.     Write-Verbose "Create directory `"$CertificateDirectory`" for storing the new certificates"
  1092.     $output = New-Item $CertificateDirectory -ItemType directory -force
  1093.     if (Test-Path $CertificateDirectory){
  1094.         if ($Production){
  1095.             Write-Verbose "Writing production certificates"
  1096.             $IntermediateCACertKeyName = "Lets Encrypt Authority X3-int"
  1097.             $IntermediateCAFileName = "$($IntermediateCACertKeyName).crt"
  1098.             $IntermediateCAFullPath = Join-Path -Path $CertificateDirectory -ChildPath $IntermediateCAFileName
  1099.             $IntermediateCASerial = "0a0141420000015385736a0b85eca708"
  1100.         } else {
  1101.             Write-Verbose "Writing test/staging certificates"
  1102.             $IntermediateCACertKeyName = "Fake LE Intermediate X1-int"
  1103.             $IntermediateCAFileName = "$($IntermediateCACertKeyName).crt"
  1104.             $IntermediateCAFullPath = Join-Path -Path $CertificateDirectory -ChildPath $IntermediateCAFileName
  1105.             $IntermediateCASerial = "8be12a0e5944ed3c546431f097614fe5"
  1106.  
  1107.         }
  1108.         Write-Verbose "Intermediate: `"$IntermediateCAFileName`""
  1109.         Get-ACMECertificate $DNSObjects[0].Alias -ExportIssuerPEM $IntermediateCAFullPath -VaultProfile $VaultName | Out-Null
  1110.        
  1111.        
  1112.         $CertificateCertKeyName = "$($SessionDateTime )_$($DNSObjects[0].DNSName)"
  1113.        
  1114.         if ($Production){
  1115.             $CertificateFileName = "$CertificateCertKeyName.crt"
  1116.             $CertificateKeyFileName = "$CertificateCertKeyName.crt.key"
  1117.             $CertificatePfxFileName = "$CertificateCertKeyName.pfx"
  1118.         } else {
  1119.             $CertificateFileName = "TST-$CertificateCertKeyName.crt"
  1120.             $CertificateKeyFileName = "TST-$CertificateCertKeyName.crt.key"
  1121.             $CertificatePfxFileName = "TST-$CertificateCertKeyName.pfx"
  1122.         }
  1123.         $CertificateFullPath = Join-Path -Path $CertificateDirectory -ChildPath $CertificateFileName
  1124.         Write-Verbose "Certificate: `"$CertificateFileName`""
  1125.         Get-ACMECertificate $DNSObjects[0].Alias -ExportCertificatePEM $CertificateFullPath -VaultProfile $VaultName | Out-Null
  1126.         $CertificateKeyFullPath = Join-Path -Path $CertificateDirectory -ChildPath $CertificateKeyFileName
  1127.         Write-Verbose "Key: `"$CertificateKeyFileName`""
  1128.         Get-ACMECertificate $DNSObjects[0].Alias -ExportKeyPEM $CertificateKeyFullPath -VaultProfile $VaultName | Out-Null
  1129.         $CertificatePfxFullPath = Join-Path -Path $CertificateDirectory -ChildPath $CertificatePfxFileName
  1130.         if ($PfxPassword){
  1131.             Write-Verbose "PFX: `"$CertificatePfxFileName`""
  1132.             Get-ACMECertificate $DNSObjects[0].Alias -ExportPkcs12 "$CertificatePfxFullPath" -CertificatePassword "$PfxPassword" -VaultProfile $VaultName | Out-Null
  1133.         } else {
  1134.             Write-Warning "No Password was specified, so a PFX certificate was not generated. If you want one run the following command:`n `
  1135.                 Get-ACMECertificate $($DNSObjects[0].Alias) -ExportPkcs12 `"$CertificatePfxFullPath`" -CertificatePassword `"P@ssw0rd`" -VaultProfile `"$VaultName`"`n`n"
  1136.         }
  1137.     }
  1138. }
  1139.  
  1140. #endregion Certificates
  1141.  
  1142. #region Upload certificates to NetScaler
  1143.  
  1144. if (-not ($CleanNS)) {
  1145.     try {
  1146.         Write-Verbose "Retreiving existing certificates"
  1147.         $CertDetails = InvokeNSRestApi -Session $NSSession -Method GET -Type sslcertkey
  1148.         Write-Verbose "Checking if IntermediateCA `"$IntermediateCACertKeyName`" already exists"
  1149.         $IntermediateCADetails = $CertDetails.sslcertkey | Where-Object {$_.serial -eq $IntermediateCASerial}
  1150.         if (-not ($IntermediateCADetails)) {
  1151.             Write-Verbose "Uploading `"$IntermediateCAFileName`" to the NetScaler"
  1152.             $IntermediateCABase64 = [System.Convert]::ToBase64String($(Get-Content $IntermediateCAFullPath -Encoding "Byte"))
  1153.             $payload = @{"filename"="$IntermediateCAFileName";"filecontent"="$IntermediateCABase64";"filelocation"="/nsconfig/ssl/";"fileencoding"="BASE64";}
  1154.             $response = InvokeNSRestApi -Session $NSSession -Method POST -Type systemfile -Payload $payload
  1155.             Write-Verbose "Succeeded"
  1156.             Write-Verbose "Add the certificate to the NetScaler config"
  1157.             $payload = @{"certkey"="$IntermediateCACertKeyName";"cert"="/nsconfig/ssl/$($IntermediateCAFileName)";}
  1158.             $response = InvokeNSRestApi -Session $NSSession -Method POST -Type sslcertkey -Payload $payload
  1159.             Write-Verbose "Succeeded"
  1160.         } else {
  1161.             $IntermediateCACertKeyName = $IntermediateCADetails.certkey
  1162.             Write-Verbose "Saving existing name `"$IntermediateCACertKeyName`" for later use"
  1163.         }
  1164.         $ExistingCertificateDetails = $CertDetails.sslcertkey | Where-Object {$_.certkey -eq $NSCertNameToUpdate}
  1165.         if (($NSCertNameToUpdate) -and ($ExistingCertificateDetails)) {
  1166.             $CertificateCertKeyName = $($ExistingCertificateDetails.certkey)
  1167.             Write-Verbose "Existing certificate `"$($ExistingCertificateDetails.certkey)`" found on the netscaler, start updating"
  1168.             try {
  1169.                 Write-Verbose "Unlinking certificate"
  1170.                 $payload = @{"certkey"="$($ExistingCertificateDetails.certkey)";}
  1171.                 $response = InvokeNSRestApi -Session $NSSession -Method POST -Type sslcertkey -Payload $payload -Action unlink
  1172.                
  1173.             } catch {
  1174.                 Write-Verbose "Certificate was not linked"
  1175.             }
  1176.             $NSUpdating = $true
  1177.         } elseif ($NSCertNameToUpdate) {
  1178.             Write-Verbose " `"$NSCertNameToUpdate`" is $($NSCertNameToUpdate.Length) long"
  1179.             if ($NSCertNameToUpdate.Length -gt 30) {
  1180.                 Write-Verbose "Name is to long, only using the first 31 characters"
  1181.                 $CertificateCertKeyName = $NSCertNameToUpdate.subString(0,31)
  1182.             } else {
  1183.                 Write-Verbose "CertkeyName is not too long, continuing"
  1184.                 $CertificateCertKeyName = $NSCertNameToUpdate
  1185.             }
  1186.             Write-Verbose "No existing certificate found, using predefined name `"$NSCertNameToUpdate`". Start configuring the certificate"
  1187.             $NSUpdating = $false
  1188.         } else {
  1189.             Write-Verbose "Start configuring the certificaten"
  1190.             $NSUpdating = $false
  1191.         }
  1192.         $CertificateCrtBase64 = [System.Convert]::ToBase64String($(Get-Content $CertificateFullPath -Encoding "Byte"))
  1193.         $CertificateKeyBase64 = [System.Convert]::ToBase64String($(Get-Content $CertificateKeyFullPath -Encoding "Byte"))
  1194.         Write-Verbose "Uploading the certificate"
  1195.         $payload = @{"filename"="$CertificateFileName";"filecontent"="$CertificateCrtBase64";"filelocation"="/nsconfig/ssl/";"fileencoding"="BASE64";}
  1196.         $response = InvokeNSRestApi -Session $NSSession -Method POST -Type systemfile -Payload $payload
  1197.        
  1198.         Write-Verbose "Uploading the certificate key"
  1199.         $payload = @{"filename"="$CertificateKeyFileName";"filecontent"="$CertificateKeyBase64";"filelocation"="/nsconfig/ssl/";"fileencoding"="BASE64";}
  1200.         $response = InvokeNSRestApi -Session $NSSession -Method POST -Type systemfile -Payload $payload
  1201.         Write-Verbose "Finished uploading"
  1202.         if ($NSUpdating) {
  1203.             Write-Verbose "Update the certificate and key to the NetScaler config"
  1204.             $payload = @{"certkey"="$CertificateCertKeyName";"cert"="/nsconfig/ssl/$($CertificateFileName)";"key"="/nsconfig/ssl/$($CertificateKeyFileName)"}
  1205.             $response = InvokeNSRestApi -Session $NSSession -Method POST -Type sslcertkey -Payload $payload -Action update
  1206.             Write-Verbose "Succeeded"
  1207.    
  1208.         } else {
  1209.             Write-Verbose "Add the certificate and key to the NetScaler config"
  1210.             $payload = @{"certkey"="$CertificateCertKeyName";"cert"="/nsconfig/ssl/$($CertificateFileName)";"key"="/nsconfig/ssl/$($CertificateKeyFileName)"}
  1211.             $response = InvokeNSRestApi -Session $NSSession -Method POST -Type sslcertkey -Payload $payload
  1212.             Write-Verbose "Succeeded"
  1213.         }
  1214.         Write-Verbose "Link `"$CertificateCertKeyName`" to `"$IntermediateCACertKeyName`""
  1215.         $payload = @{"certkey"="$CertificateCertKeyName";"linkcertkeyname"="$IntermediateCACertKeyName";}
  1216.         $response = InvokeNSRestApi -Session $NSSession -Method POST -Type sslcertkey -Payload $payload -Action link
  1217.         Write-Verbose "Succeeded"
  1218.         if ($SaveNSConfig) {
  1219.             Write-Verbose "Saving NetScaler configuration"
  1220.             InvokeNSRestApi -Session $NSSession -Method POST -Type nsconfig -Action save
  1221.         }
  1222.         ""
  1223.         Write-Host -ForeGroundColor Green "Finished with the certificates!"
  1224.         if (-not $Production){
  1225.             Write-Host -ForeGroundColor Green "You are now ready for the Production version!"
  1226.             Write-Host -ForeGroundColor Green "Add the `"-Production`" parameter and rerun the same script."
  1227.         }
  1228.     } catch {
  1229.         throw "ERROR. Certificate completion failed, details: $($_.Exception.Message | Out-String)"
  1230.     }
  1231. }
  1232. #endregion Upload certificates to NetScaler
Add Comment
Please, Sign In to add comment