How to use Get-AzKeyVaultSecret in Powershell Azure Function 2.x - powershell

I'm setting up a Powershell Azure Function that needs keys from an Azure KeyVault. Most of the keys can be retrieved by using the #Microsoft.KeyVault(SecretUri='MySecretUriWithVersion') method.
One of the keys changes frequently. Hence the SecretUri cannot be used.
All keys are stored in the same KeyVault and the Function has a MSI that can read, list and change all keys.
I'm working with a refresh token that needs to be updated. This value is renewed every time my code runs and needs to be updated in the keyvault
Connect-AzAccount -Identity
#Works
Get-AzKeyVault -VaultName $VaultName -ResourceGroupName $rgName
#Not working
Get-AzKeyVaultSecret -VaultName $VaultName -Name $KeyName
Expected output: The code retrieves the key.
Actual output: ERROR: Operation returned an invalid status code
'Unauthorized' Microsoft.Azure.WebJobs.Script.Rpc.RpcException :
Result: ERROR: Operation returned an invalid status code
'Unauthorized' Exception: Operation returned an invalid status code
'Unauthorized' Stack: at
Microsoft.Azure.KeyVault.KeyVaultClient.GetSecretWithHttpMessagesAsync(String
vaultBaseUrl, String secretName, String secretVersion, Dictionary`2
customHeaders, CancellationToken cancellationToken)
dlet.ProcessRecord()

Taken from Microsoft: https://learn.microsoft.com/en-us/azure/key-vault/quick-create-powershell
Have you tried dot notation on retrieving those keys?
(Get-AzKeyVaultSecret -vaultName $VaultName -name $KeyName).SecretValueText
If that doesn't work, you may look at this github issue regarding ManagedAppServices: https://github.com/Azure/azure-powershell/issues/8983
Seems to be the same issue you're having.

There is now a new method for this.
$secret = Get-AzKeyVaultSecret -VaultName "<your-unique-keyvault-name>" -Name "ExamplePassword"
$ssPtr = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($secret.SecretValue)
try {
$secretValueText = [System.Runtime.InteropServices.Marshal]::PtrToStringBSTR($ssPtr)
} finally {
[System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($ssPtr)
}
Write-Output $secretValueText
Refer to: https://learn.microsoft.com/en-us/azure/key-vault/secrets/quick-create-powershell

The original error is fixed in Azure Functions now.
It is still necessary to retrieve keys that are updated frequently in key vault. To do that, I use this:
(Get-AzKeyVaultSecret MyVaultName -Name MySecretName).secretvalue | ConvertFrom-SecureString -AsPlainText

Also, please refer to this link if you face any issues due to secretValueText being deprecated.

Related

Azure Function Powershell using Ms-Graph and certificate authentication

I want to run an Azure function against AAD to query some users there. Authentications should happen via certificate as shown in the code snippet. I keep googling around but cannot find the proper way of doing it. The code shown below ends up in an exection - how do I need to parse the cert coming from Azure Key Vault?
...
$tenant_id = "something-else"
$app_reg_clientid = "something"
$cert = Get-AzKeyVaultCertificate -VaultName "my-kv" -Name "my-kv-cert"
Connect-MgGraph -ClientId $app_reg_clientid -TenantId $tenant_id -Certificate $cert
...
ERROR: Cannot bind parameter 'Certificate'. Cannot convert the "Microsoft.Azure.Commands.KeyVault.Models.PSKeyVaultCertificate" value of type "Microsoft.Azure.Commands.KeyVault.Models.PSKeyVaultCertificate" to type "System.Security.Cryptography.X509Certificates.X509Certificate2". Exception ...
I think this summarizes the things: https://briantjackett.com/2018/07/25/azure-functions-calling-azure-ad-application-with-certificate-authentication/
Also I am missing a lot of steps in your post when comparing against the official documentation: https://learn.microsoft.com/en-us/powershell/microsoftgraph/app-only?view=graph-powershell-1.0&tabs=azure-portal
The official docs use the subjectname from the certificate in the parameter: -CertificateName
You are using; Well I don't know exactly, but if i'm not mistaken; You are using the thumbprint which is pulled from the Keyvault for parameter: -Certificate
I think you should double back and check all the steps from the official docs for your flow.

Azure AD - Cannot set Application Registration Key Credential with Type "Sign"

I'm trying to set a custom signing key for an Azure AD Application Registration. However, I get a confusing error message and cannot complete the request.
I tried to set the credential using multiple strategies:
PowerShell New-AzureADApplicationKeyCredential command
Microsoft Graph API
Manipulating the Application Registrations Manifest directly in Azure Portal
Microsoft Graph returns a simple "Bad Request", whereas PowerShell and Azure Portal are more specific in their responses:
"The value for the property "usage" in one of your credentials is invalid. Acceptable values are Sign, Verify."
The interesting thing about this error is that I am specifying the usage as "Sign".
PowerShell code snippet:
$appObjectID = $appRegistration.ObjectId
$cer = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2
$cer.Import("<path-to-certificate>")
$bin = $cer.GetRawCertData()
$base64Value = [System.Convert]::ToBase64String($bin)
$bin = $cer.GetCertHash()
$base64Thumbprint = [System.Convert]::ToBase64String($bin)
New-AzureADApplicationKeyCredential `
-ObjectId $appObjectID `
-CustomKeyIdentifier $base64Thumbprint `
-Type AsymmetricX509Cert `
-Usage Sign `
-Value $base64Value `
-StartDate $cer.GetEffectiveDateString() `
-EndDate $cer.NotAfter.ToString()
Error message:
Code: Request_BadRequest
Message: The value for the property "usage" in one of your credentials is invalid. Acceptable values are Sign, Verify.
RequestId: <id>
DateTimeStamp: <timestamp>
Details: PropertyName - keyCredentials.keyId, PropertyErrorCode - InvalidKeyUsage
HttpStatusCode: BadRequest
HttpStatusDescription: Bad Request
HttpResponseStatus: Completed`
This is based on the documentation: MS Docs: New-AzureADApplicationKeyCredential. However, I think there is a mistake in this documentation, since they use a randomly generated GUID as input for the parameter ObjectID, which should be the ObjectID of the Application Registration I want to add the new key credential to. So I replaced this keyId with the ObjectId of my Application Registration. (If I directly use the code from MS Docs, I get a "Request_ResourceNotFound" error because the command can't find the Application Registration with this random GUID in Azure AD.)
Things I have tried:
Change -Usage Sign to -Usage "Sign"
Adding a "Verify" credential to the App (works as expected) with this command
When I try to directly modify the Manifest in Azure Portal, I basically get the same error message:
Failed to update <app-name> application. Error detail: The value for the property "usage" in one of your credentials is invalid. Acceptable values are Sign, Verify.
Screenshot from error in Azure Portal
Is there maybe a issue that some parameters cannot be used this way when setting a "Sign" credential?
Thanks in advance for any help and regards!
Thanks to Ash (see his comment to my initial question) I found the solution in this article. I followed the tutorial and could set the "Sign" Key Credential using Graph API after also including a "Verify" Key Credential and a corresponding Password Credential into the request body.

Missing cluster cert causes Add-AzServiceFabricClusterCertificate to fail: Object reference not set to an instance of an object

I'm fairly new to Service Fabric, so I'm not sure if this is an issue with the cmdlet or if this is a miss on my part. I am using Az.ServiceFabric module version 2.0.2 and the Az module version 3.8.0.
I am trying to use the Add-AzServiceFabricClusterCertificate cmdlet to add a secondary certificate that I've already created in my Azure KeyVault to my cluster. When I run the cmdlet, it fails with this error (running with Debug gave me more stack detail):
DEBUG: AzureQoSEvent: CommandName - Add-AzServiceFabricClusterCertificate; IsSuccess - False; Duration -
00:00:07.3059582;; Exception - System.NullReferenceException: Object reference not set to an instance of an object.
at Microsoft.Azure.Commands.ServiceFabric.Commands.ServiceFabricClusterCmdlet.GetClusterType(Cluster
clusterResource)
at Microsoft.Azure.Commands.ServiceFabric.Commands.AddAzureRmServiceFabricClusterCertificate.ExecuteCmdlet()
at Microsoft.WindowsAzure.Commands.Utilities.Common.AzurePSCmdlet.ProcessRecord();
Looking at the code for this cmdlet, I noticed that it's probably failing because the cluster resource that gets passed into GetClusterType does not have its Certificate member, so it fails when it tries to check the Certificate.Thumbprint and Certificate.ThumbprintSecondary:
internal ClusterType GetClusterType(Cluster clusterResource)
{
if (string.IsNullOrWhiteSpace(clusterResource.Certificate.Thumbprint) &&
string.IsNullOrWhiteSpace(clusterResource.Certificate.ThumbprintSecondary))
{
return ClusterType.Unsecure;
}
else
{
return ClusterType.Secure;
}
}
The cluster that gets passed into GetClusterType is retrieved in the same manner as in the Get-AzServiceFabricCluster cmdlet, so when I run that cmdlet for the cluster that I'm trying to add the certificate to, I noticed that my Certificate field is empty in the response. I'm guessing that's what's causing the NullRef exception. Here's that relevant snippet:
AzureActiveDirectory :
TenantId : xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
ClusterApplication : xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
ClientApplication : xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
Certificate :
CertificateCommonNames : Microsoft.Azure.Management.ServiceFabric.Models.ServerCertificateCommonNames
ClientCertificateCommonNames :
ClientCertificateThumbprints :
I'm wondering if it's expected that the Certificate field would be empty when I'm using the Get-AzServiceFabricCluster cmdlet and if that is indeed the cause of my Add-AzServiceFabricClusterCertificate cmdlet failing. When I look at the cluster's Security blade in Azure Portal, I do see the Primary Cluster Certificate with which I originally created the cluster, and this is the cert that I use when deploying and doing other cluster operations. However, I did notice that the cert thumbprint field is empty when viewing the certificate from the portal. I would expect to see this certificate when using Get-AzServiceFabricCluster, but it comes up empty. Is this certificate missing from my Get-AzServiceFabricCluster cmdlet possible to fix through the portal or with another cmdlet?
It looks like your cluster is configured to find certificates by common name, rather than thumbprint. I'm guessing this based on the fact your portal doesn't show a thumbprint against the certificate, in addition to the snippet you have posted.
If this is the case, there's no need to update your cluster configuration with a new certificate when the old certificate has expired - Instead you need to install the certificate only into your VMSS vault. Once you add the new certificate to the VMSS, Service Fabric will automatically use the later expiring certificate.
You must always ensure you have at least one valid certificate installed on your VMSS with the common name configured in your cluster.
PS to upload certificate KV and install onto VMSS:
$subscriptionId = "sub-id"
$vmssResourceGroupName = "vmss-rg-name"
$vmssName = "vmss-name"
$vaultName = "kv-name"
$primaryCertName = "kv-cert-name"
$certFilePath = "...\.pfx"
$certPassword = ConvertTo-SecureString -String "password" -AsPlainText -Force
# Sign in to your Azure account and select your subscription
Login-AzAccount -SubscriptionId $subscriptionId
# Update primary certificate within the Key Vault
$primary = Import-AzKeyVaultCertificate `
-VaultName $vaultName `
-Name $primaryCertName `
-FilePath $certFilePath `
-Password $certPassword
$certConfig = New-AzVmssVaultCertificateConfig -CertificateUrl $primary.SecretId -CertificateStore "My"
# Get VM scale set
$vmss = Get-AzVmss -ResourceGroupName $vmssResourceGroupName -VMScaleSetName $vmssName
# Add new certificate version
$vmss.VirtualMachineProfile.OsProfile.Secrets[0].VaultCertificates.Add($certConfig)
# Update the VM scale set
Update-AzVmss -ResourceGroupName $vmssResourceGroupName -Verbose `
-Name $vmssName -VirtualMachineScaleSet $vmss
For more info, I wrote a blog post on switching from thumbprint to common name.
The official docs are also a good reference.

Cannot Delete All Azure Website Connection Strings

For a website my team are currently working on we're trying to write some PowerShell automation to temporarily override a connection string which is otherwise usually in Web.config, and then at a later point delete it.
However, using both the PowerShell cmdlets and the Azure REST APIs, we don't seem to be able to ever reset the connection strings set to be empty. Whenever we try to delete it either the command says it succeeds but doesn't delete it, or the requests is rejected as invalid.
It's possible to delete all the connection strings using the Management Portal, so what are we missing that means we can't do it programmatically?
Here's an example PowerShell command we're using that isn't doing what we want:
$website = Get-AzureWebsite $websiteName -slot $websiteSlot
$connectionStrings = $website.ConnectionStrings
$result = $connectionStrings.Remove(($connectionStrings | Where-Object {$_.Name -eq $connectionStringName}))
Set-AzureWebsite $websiteName -slot $websiteSlot -ConnectionStrings $connectionStrings
I know this is really late.
I have faced the same situation.We are able to update the App settings but not the connection strings.I found the solution.Since it doesn't have an answer, I thought it would be good to update it..
As per the git-hub source, the hash-table(nested) needs to be in the format of
#{ KeyName = #{ Type = "MySql"; Value = "MySql Connection string"}};
after which I ran the code to update the App, it worked. Below is the whole code
$list = #{ ConnectionString1 = #{ Type = "MySql"; Value = "MySql Connection string"}};
set-AzureRMWebAppSlot -ResourceGroupName "resourceGroup" -Name "devTest" -ConnectionStrings $list -slot "production"

Create Azure AD ClientCredentials Key from PowerShell

In the Azure Portal I can create an Application, Key and Permissions to the Graph API.
I can get a Token using:
AuthenticationContext ac = new AuthenticationContext("https://login.windows.net/graphDir1.onmicrosoft.com");
ClientCredential cc = new ClientCredential("b3b1fc59-84b8-4400-a715-ea8a7e40f4fe", "FStnXT1QON84B5o38aEmFdlNhEnYtzJ91Gg/JH/Jxiw=");
AuthenticationResult authResult = ac.AcquireToken("https://graph.windows.net", cc);
Using the Azure Active Directory Module for Windows PowerShell I can create a new Symmetric Key.
New-MsolServicePrincipalCredential -AppPrincipalId ??? -Type Symmetric
Using the key returned from this in the code above returns the error:
AdalServiceException: AADSTS70002: Error validating credentials. AADSTS50012: Invalid client secret is provided.
This used to work with a previous version of ADAL using instead of ClientCredential, SymmetricKeyCredential but that class no longer exists.
Is there a way to generate a key from PowerShell that works with the code above?
Please try using Password as the key type:
New-MsolServicePrincipalCredential -AppPrincipalId $appId `
-Type Password `
-StartDate ([DateTime]::Now.AddMinutes(-5)) `
-EndDate ([DateTime]::Now.AddMonths(1)) `
-Value "$newPassword"
Hope this helps