Access Denied using Import-AzKeyVaultCertificate - powershell

I have a seemingly simple script for importing a PFX into Azure Key Vault. The service principal I am using is set to the Key Vault contributor role and has the access policies Get, List, Update, Create and Import. I am using certificate authentication when connecting the service principal. However, I get a very unhelpful error:
Import-AzKeyVaultCertificate : Access denied.
At line:1 char:1
+ Import-AzKeyVaultCertificate -VaultName $keyVault -Name 'AzureAuth' - ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : CloseError: (:) [Import-AzKeyVaultCertificate], CryptographicException
+ FullyQualifiedErrorId : Microsoft.Azure.Commands.KeyVault.ImportAzureKeyVaultCertificate
My Script:
$PFX = "$env:TEMP\cert-lab.pfx"
$tenantID = 'xxx'
$AppID = 'xxx'
$keyVault = 'LabKV'
$assetName = 'AzureCert'
$EncryptPass = 'PFXpassword123'
## Clear the authentication context
Clear-AzContext -Force
$PFXPass = ConvertTo-SecureString -String $EncryptPass -AsPlainText -Force
$pfxCert = Import-PfxCertificate -FilePath $PFX -CertStoreLocation Cert:\CurrentUser\My -Password $PFXPass -Exportable
$ctx = Connect-AzAccount -ServicePrincipal -Tenant $tenantID -CertificateThumbprint $pfxCert.Thumbprint -ApplicationId $AppID
Import-AzKeyVaultCertificate -VaultName $keyVault -Name $assetName -FilePath $PFX -Password $PFXPass -DefaultProfile $ctx
Interestingly, if I manually import the cert through the console, I can use the exact same code with Get-AzKeyVaultCertificate instead of Import-AzKeyVaultCertificate and successfully retrieve the certificate. This tells me that the Service Principal at least has access to retrieve certificates. I'm still at a loss as to what is causing the Import to fail.

So I ended up buying a support plan and opening a case with Microsoft. Even though I was able to directly import cert-lab.pfx and was able to verify its contents with OpenSSL, for whatever reason, Import-AzKeyVaultCertificate just did not like the file. They had me create a new file by exporting from my CAPI store to cert2.pfx. The import command worked flawlessly with the new file. I shrugged my shoulders, deleted the old file and decided to move on with my life.

Related

PowerShell: System cannot find file when user not logged on

When I try to use Import-PfxCertificate I get an error when the user account I'm running the script with isn't logged on:
Import-PfxCertificate : The system cannot find the file specified. (Exception from HRESULT: 0x80070002)
When I open an RDP session to the server the script runs at on that user account, it works without problems. The weird thing is that Test-Path $certfile -PathType Leaf returns true in both cases. What is going on?
Test-Path $certfile -PathType Leaf #always true
Import-PfxCertificate -FilePath $certfile -CertStoreLocation Cert:\CurrentUser\My -Password $Secure_String_Pwd
So the script is run as a background job on a timer on the server and when,
case 1: user account that is used to run the script has an active RDP session, the script runs fine.
case 2: user account that is used to run the script doesn't have an active RDP session, the script fails.
Edit: I found something in error[1].
New-Item : Could not find a part of the path 'C:\Users\Default\.Azure\AzInstallationChecks.json'.
At C:\Program Files\WindowsPowerShell\Modules\AzureRM.profile\5.8.2\StartupScripts\AzureRmError.ps1:17 char:9
What's this then?
I ran your command on my console, and I had no issues with the import. I am an admin on the machine that contains my console. I tried this with the powerShell session being elevated and non-elevated. Both worked.
$certfile = "C:\temp\cert.pfx"
$secure_string_pwd = convertto-securestring -string "password" -asplaintext -force
Import-PfxCertificate -FilePath $certfile -CertStoreLocation Cert:\CurrentUser\My -Password $Secure_String_Pwd
If this is not helpful at all, I will just delete this.
Try this piece of code and see it it works. Please post a complete exception if it doesn't work.
try
{
$Secure_String_Pwd = ConvertTo-SecureString 'DDDDD12345' -AsPlainText -Force
Import-PfxCertificate -FilePath 'C:\LocalPath\local.pfx' -CertStoreLocation Cert:\LocalMachine\My -Password $Secure_String_Pwd
$thumbprint = (Get-ChildItem -Path cert:\LocalMachine\my| Where-Object {$_.Subject -eq "CN=certificateNameWithoutextension"}).Thumbprint
# code for connect-Azure RM account
Connect-AzureRmAccount -ApplicationId $appid -CertificateThumbprint $thumbprint -Tenant $tenant -ServicePrincipal
}
catch
{
Write-Output $_
echo $_.Exception|format-list -force
}
Hope it helps.
It didn't help. The full exception is
PSParentPath: Microsoft.PowerShell.Security\Certificate::LocalMachine\My
Thumbprint Subject
---------- -------
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX CN=derp
Connect-AzureRmAccount : The system cannot find the file specified.
At C:\temp\script.ps1:8 char:5
+ Connect-AzureRmAccount -ApplicationId $appid -CertificateThumbpri ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : CloseError: (:) [Connect-AzureRmAccount], Crypto
graphicException
+ FullyQualifiedErrorId : Microsoft.Azure.Commands.Profile.ConnectAzureRmA
ccountCommand
Anyway, I went around the problem using C#.

Invalid provider type specified. CryptographicException

I am trying to run the script GetAppConfigSettings.ps1 from Microsoft docs help setting up a Key Vault
The script contains the following
# **********************************************************************************************
# Prep the cert credential data
# **********************************************************************************************
$certificateName = "$applicationName" + "cert"
$myCertThumbprint = (New-SelfSignedCertificate -Type Custom -Subject "$certificateName"-KeyUsage DigitalSignature -KeyAlgorithm RSA -KeyLength 2048 -CertStoreLocation "Cert:\CurrentUser\My" -Provider "Microsoft Enhanced Cryptographic Provider v1.0" ).Thumbprint
$x509 = (Get-ChildItem -Path cert:\CurrentUser\My\$myCertthumbprint)
$password = Read-Host -Prompt "Please enter the certificate password." -AsSecureString
# Saving the self-signed cert and pfx (private key) in case it's needed later
Export-Certificate -cert $x509 -FilePath ".\$certificateName.cer"
Export-PfxCertificate -Cert $x509 -FilePath ".\$certificateName.pfx" -Password $password
Running the script ( after setting the variables) produces the following error
New-SelfSignedCertificate : CertEnroll::CX509Enrollment::_CreateRequest: Provider type not defined.
0x80090017 (-2146893801 NTE_PROV_TYPE_NOT_DEF)
At \\tsclient\E\EShared\Dev\Microsoft.Azure.KeyVault.Samples-2016.11.22
(1)\Microsoft.Azure.KeyVault.Samples\scripts\GetAppConfigSettings.ps1:38 char:22
+ ... umbprint = (New-SelfSignedCertificate -Type Custom -Subject "$certifi ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [New-SelfSignedCertificate], Exception
+ FullyQualifiedErrorId : System.Exception,Microsoft.CertificateServices.Commands.NewSelfSignedC
ertificateCommand
[Update]
Microsoft Support advised me to change the provider to "Microsoft Platform Crypto Provider"
However I still get the error.
For Powershell, $PSVersionTable reports 5.1.17134.112
I have Version 5.7.0 of AzureRM installed
Microsoft support helped me out with this line
$myCertThumbprint = (New-SelfSignedCertificate -CertStoreLocation Cert:\CurrentUser\My
-subject MyCert -KeyExportPolicy Exportable -NotAfter (Get-Date).AddYears(10)
-Type CodeSigningCert -KeySpec Signature).Thumbprint
The AuthClientId and AuthCertThumbprint values I need for the HelloKeyVault app.config are created.
The AuthClientId displays in the portal as the Application ID and is vissible in the Registered app settings.
To get to it click Azure Active Directory -> App registrations
Then click View all applications
click on the application then settings
To see the Thumbprint doe the same and then click Keys
I can see AuthClientId
Please use this sample to learn how to use Key Vault with DotNet and authenticate to Azure Active Directory with a Service Principal's Certificate
https://github.com/Azure-Samples/key-vault-dotnet-quickstart

Set-AzureRmContext error when executed within an Azure Automation Runbook

Update:
Seems like someone else had the same issue and reported it.
I am facing an issue with a simple PowerShell script when invoking it from an Azure Automation Runbook. The same piece of code works flawless when running it locally.
I have added a Service Principal in an Azure Active Directory (hosted in Azure German Cloud) with password credential and grant it contributor access to a subscription (also hosted in Azure German Cloud).
The Azure Automation service is hosted in North Europe since it's currently not available in the Azure German Cloud.
All I try to do is to login to my subscription with the above mentioned principal using the Add-AzureRmAccount cmdlet. After that I try to set the current context using the Set-AzureRmContext and getting the following error message:
Set-AzureRmContext : Please provide a valid tenant or a valid subscription.
At line:26 char:1
+ Set-AzureRmContext -TenantId $TenantId -Su ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : CloseError: (:) [Set-AzureRmContext], ArgumentException
+ FullyQualifiedErrorId : Microsoft.Azure.Commands.Profile.SetAzureRMContextCommand
Here is the script I try to run (left the configuration blank):
$TenantId = ""
$ApplicationId = ""
$ClientSecret = ""
$SubscriptionId = ""
$secpasswd = ConvertTo-SecureString $ClientSecret -AsPlainText -Force
$mycreds = New-Object System.Management.Automation.PSCredential ($ApplicationId , $secpasswd)
Add-AzureRmAccount -ServicePrincipal -Environment 'AzureGermanCloud' -Credential $mycreds -TenantId $TenantId
Set-AzureRmContext -TenantId $TenantId -SubscriptionId $SubscriptionId
I also tried to use Login-AzureRmAccount without success. Also I am able to use the Get-AzureRmResourceGroup cmdlet to retrieve the resource groups so the login seems to work.
All Azure modules are updated to the latest version.
TLTR:
My main goal is to start a SQL export job using the New-AzureRmSqlDatabaseExport from the runnbook but it seems like the above mentioned error causes the cmdlet to fail with the following message:
New-AzureRmSqlDatabaseExport : Your Azure credentials have not been set up or have expired, please run
Login-AzureRMAccount to set up your Azure credentials.
At line:77 char:18
+ ... rtRequest = New-AzureRmSqlDatabaseExport -ResourceGroupName $Resource
I had the same issue a few weeks ago and what worked was to first login to Azure account (which I think you already did) using:
Login-AzureRmAccount
Then get the subscription ID from Azure and use select the subscription using the ID instead of the name as follows:
Select-AzureRmSubscription -SubscriptionId {insert-subscription-id}
Below is the code that worked for me (regular dc regions). If it doesn't work, go to the Automation Account >> Modules >> Update Azure Modules.
$ClientSecret = ""
$ApplicationId = ""
$SubscriptionId = ""
#New PSCredential Object
$secpasswd = ConvertTo-SecureString $ClientSecret -AsPlainText -Force
$mycreds = New-Object System.Management.Automation.PSCredential ($ApplicationId , $secpasswd)
#Login to subscription
Login-AzureRmAccount -Credential $mycreds -SubscriptionId $SubscriptionId
#Export Database
New-AzureRmSqlDatabaseExport -ResourceGroupName "<RG>" -ServerName "<SQLSERVERNAME>" -DatabaseName "<DATABASENAME>" -StorageKeyType "StorageAccessKey" -StorageKey "<STRKEY>" -StorageUri "<URITOFILE>" -AdministratorLogin "<DBLOGIN>" -AdministratorLoginPassword "<DBPASS>"
Update
Maybe running with a Run As Account can be a workaround for the issue. Create one by navigating to the Azure Automation Account >> Account Settings >> Run As Accounts. Here's an example code.
# Authenticate to Azure with service principal and certificate, and set subscription
$connectionAssetName = "AzureRunAsConnection"
$conn = Get-AutomationConnection -Name $ConnectionAssetName
Add-AzureRmAccount -ServicePrincipal -Tenant $conn.TenantID -ApplicationId $conn.ApplicationId -CertificateThumbprint $conn.CertificateThumbprint -ErrorAction Stop | Write-Verbose
Set-AzureRmContext -SubscriptionId $conn.SubscriptionId -ErrorAction Stop | Write-Verbose
It looks like this is a known issue and I wasn't able to find a fix for that. But there are two workarounds:
Using a Hybrid Runnbook Worker (mentioned by Walter - MSFT)
Using a RunAsAccount with certificate credentials (mentioned by Bruno Faria)
It is important to specify the -Environment parameter. Otherwise I got the following exception:
Login-AzureRmAccount : AADSTS90038: Confidential Client is not
supported in Cross Cloud request.
Here is the code I am using to login to AzureGermanCloud (MCD) from an Azure Runbook hosted in NorthEurope:
$connectionAssetName = "AzureRunAsConnection"
$conn = Get-AutomationConnection -Name $ConnectionAssetName
Login-AzureRmAccount `
-ServicePrincipal `
-CertificateThumbprint $conn.CertificateThumbprint `
-ApplicationId $conn.ApplicationId `
-TenantId $conn.TenantID `
-Environment AzureGermanCloud
When you login your Azure account, you could use specified subscription id. You could try following script.
$subscriptionId=""
$tenantid=""
$clientid=""
$password=""
$userPassword = ConvertTo-SecureString -String $password -AsPlainText -Force
$userCredential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $clientid, $userPassword
Add-AzureRmAccount -TenantId $tenantid -ServicePrincipal -SubscriptionId $subscriptionId -Credential $userCredential -Environment 'AzureGermanCloud'

Add-AzureRmAccount : Sequence contains no element (Not working for Gmail accounts too)

I have created a 'Free Trial' account with my personal email ID which is a Gmail ID. I'm getting the error :
Add-AzureRmAccount : Sequence contains no elements At line:1 char:1
+ Add-AzureRmAccount -Credential $cred
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : CloseError: (:) [Add-AzureRmAccount], AadAuthenticationFailedException
+ FullyQualifiedErrorId : Microsoft.Azure.Commands.Profile.AddAzureRMAccountCommand
The code I'm running is
$username = "abc#gmail.com"
$password = "something"
$secpass = $password | ConvertTo-SecureString -AsPlainText -Force
$cred = new-object -typename System.Management.Automation.PSCredential -argumentlist $username, $secpass
Add-AzureRmAccount -Credential $cred
Are there certain type of accounts/subscriptions for which logging in like this is supposed to work?
Windows Live ID credentials cannot be used for a non-interactive login. This error message is described as part of this issue which has been raised because it needs improving.
I think you either need to use Login-AzureRmAccount to login interactively or create a Service Principal for login, per this guide: https://learn.microsoft.com/en-us/powershell/azure/authenticate-azureps?view=azurermps-4.2.0
Log in with a service principal
Service principals provide a way for you to create non-interactive
accounts that you can use to manipulate resources. Service principals
are like user accounts to which you can apply rules using Azure Active
Directory. By granting the minimum permissions needed to a service
principal, you can ensure your automation scripts are even more
secure.
If you don't already have a service principal, create one.
Log in with the service principal:
Login-AzureRmAccount -ServicePrincipal -ApplicationId "http://my-app" -Credential $pscredential -TenantId $tenantid

List all my Azure websites using an Azure Powershell Function

Im testing the new Azure Functions, and would like to write a function that return all my Azure Websites. But needless to say I run into some problems, and documantation is still minimal.
run.ps1
# Get the input request
$in = Get-Content $req -Raw | ConvertFrom-Json
Write-Output "Loading..."
Get-AzureRmSubscription -SubscriptionId $in.SubscriptionId | Select-AzureRmSubscription
$Result = Get-AzureWebsite
Write $Result
This function take the subscription id as a parameter, and is supposed to list the available websites. But I get this exception.
2017-06-13T12:43:57.763 Get-AzureRmSubscription : Run Login-AzureRmAccount to login.
So I tried to add Login-AzureRmAccount but then I get.
2017-06-13T12:45:04.959 Login-AzureRmAccount : Error HRESULT E_FAIL has been returned from a call to a COM component.
And that is where I stand now.
Update
After help from #4c74356b41 I now am able to login. My code for logging in looks like this.
$subscriptionId = "<SubscriptionId>"
$tenantid = "<TenantId>"
$clientid = "<ApplicationId>"
$password = "<Password>"
$userPassword = ConvertTo-SecureString -String $password -AsPlainText -Force
$userCredential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $clientid, $userPassword
Add-AzureRmAccount -TenantId $tenantid -ServicePrincipal -SubscriptionId $subscriptionId -Credential $userCredential
I can see that this work when I test the code. But as soon as I add this line.
Select-AzureSubscription -Current -SubscriptionId $subscriptionId
I get this exception.
Select-AzureSubscription : The subscription id <SubscriptionId> doesn't exist.
Parameternavn: id
At line:11 char:1
+ Select-AzureSubscription -Current -SubscriptionId $subscriptionId
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : CloseError: (:) [Select-AzureSubscription], ArgumentException
+ FullyQualifiedErrorId : Microsoft.WindowsAzure.Commands.Profile.SelectAzureSubscriptionCommand
I also tried to add this line.
Get-AzureRmSubscription –SubscriptionId $subscriptionId | Select-AzureRmSubscription
Which look like is working, it only thows a warning WARNING: Unable to acquire token for tenant 'Common' but still list the correct subscription details without any exceptions.
Then when I try
Get-AzureWebsite
I get this exception.
Get-AzureWebsite : No default subscription has been designated. Use Select-AzureSubscription -Default <subscriptionName> to set the default subscription.
At line:15 char:1
+ Get-AzureWebsite
+ ~~~~~~~~~~~~~~~~
+ CategoryInfo : CloseError: (:) [Get-AzureWebsite], ApplicationException
+ FullyQualifiedErrorId : Microsoft.WindowsAzure.Commands.Websites.GetAzureWebsiteCommand
Well, how do you expect to work with your subscription without authenticating? would you like anybody to be able to modify your resources without any validation at all? so you need to authenticate before doing anything.
Working with powershell in Azure Function is no different from working with powershell on your machine (except module management).
To login you could use service principal auth ang login with something like:
Add-AzureRmAccount -TenantId $tenantid -ServicePrincipal -SubscriptionName $name `
-Credential ([pscredential]::new($clientid,(ConvertTo-SecureString -String $password -AsPlainText -Force)))
you can replace variables (hardcoded in the code) with environment variables.
Add 4c74356b41's answer, Get-AzureWebsite is an Azure Classic mode cmdlet. Now, you login your ARM subscription, so, it requires you login classic subscription. Select-AzureSubscription is a classic cmdlet that use to select classic subscription.
In Azure ARM mode, website is renamed Webapp, you could check Azure App Service announcement.
So, if you want to list your all webapp, you should use cmdlet Get-AzureRmWebApp.
More information please refer to this link: Using Azure Resource Manager-Based PowerShell to Manage Azure Web Apps.