title: Setting private key permissions with PowerShell
body: <p>I have a PowerShell script that installs pfx certificate into the <em>LocalMachine</em> certificate store. The function looks like this:</p>
  
  <pre><code>function Add-Certificate {
  param
  (
      [Parameter(Position=1, Mandatory=$true)]
      [ValidateNotNullOrEmpty()]
      [string]$pfxPath,
  
      [Parameter(Position=2, Mandatory=$true)]
      [ValidateNotNullOrEmpty()]
      [string]$pfxPassword
  )
  
      Write-Host "Installing certificate" -ForegroundColor Yellow
  
      try 
      {
          $pfxcert = new-object system.security.cryptography.x509certificates.x509certificate2
          $pfxcert.Import($pfxPath, $pfxPassword, [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]"PersistKeySet")
  
          $store = new-object system.security.cryptography.X509Certificates.X509Store -argumentlist "MY", LocalMachine
          $store.Open([System.Security.Cryptography.X509Certificates.OpenFlags]"ReadWrite");
          $store.Add($pfxcert);
          $store.Close();
  
          return $pfxcert
      }
      catch 
      {
          throw
      }
  }
  </code></pre>
  
  <p>When I open the Certificate Manager to verify the installation I can see that it has installed correctly.</p>
  
  <p>The next step in my process is to assign permissions to the certificate to a service account.</p>
  
  <pre><code>function Set-CertificatePermission
  {
      param
      (
          [Parameter(Position=1, Mandatory=$true)]
          [ValidateNotNullOrEmpty()]
          [string]$pfxThumbPrint,
  
          [Parameter(Position=2, Mandatory=$true)]
          [ValidateNotNullOrEmpty()]
          [string]$serviceAccount
      )
  
      $cert = Get-ChildItem -Path cert:\LocalMachine\My | Where-Object -FilterScript { $PSItem.ThumbPrint -eq $pfxThumbPrint; };
  
      # Specify the user, the permissions and the permission type
      $permission = "$($serviceAccount)","Read,FullControl","Allow"
      $accessRule = New-Object -TypeName System.Security.AccessControl.FileSystemAccessRule -ArgumentList $permission;
  
      # Location of the machine related keys
      $keyPath = $env:ProgramData + "\Microsoft\Crypto\RSA\MachineKeys\";
      $keyName = $cert.PrivateKey.CspKeyContainerInfo.UniqueKeyContainerName;
      $keyFullPath = $keyPath + $keyName;
  
      try
      {
          # Get the current acl of the private key
          # This is the line that fails!
          $acl = Get-Acl -Path $keyFullPath;
  
          # Add the new ace to the acl of the private key
          $acl.AddAccessRule($accessRule);
  
          # Write back the new acl
          Set-Acl -Path $keyFullPath -AclObject $acl;
      }
      catch
      {
          throw $_;
      }
  }
  </code></pre>
  
  <p>This function fails. Specifically, this function fails when trying to evaluate the Get-Acl command, with the following error: <strong>Get-Acl : Cannot find path 'C:\ProgramData\Microsoft\Crypto\RSA\MachineKeys\59f1e969a4f7e5de90224f68bc9be536_1d508f5e-0cbc-4eca-a402-3e55947faa3b'</strong></p>
  
  <p>As it turns out the key file has been installed into my roaming profile <strong>C:\Users\MyUserName\AppData\Roaming\Microsoft\Crypto\RSA\S-1-5-21-1259098847-1967870486-1845911597-155499</strong></p>
  
  <p>I'm sure there is something wrong with the Add-Certificate function, but I cannot figure out what it is. How do I force it to install the key file in the C:\ProgramData\Microsoft\Crypto\RSA\MachineKeys directory?</p>