Assign Key Vault Secrets to an Azure Function using Azure PowerShell - azure

I am trying to automate the creation of certain azure resources via an Azure PowerShell script that is triggered from an Azure DevOps release pipeline. I want to create a function app, and automatically integrate reading right access to secrets in an already existing Key Vault. This Key Vault is in the same Azure subscription.
While I can create most resources following the documentation, there seems to be a lack of documentation regarding the creation of certain resources using Azure PowerShell (or I can't find it).
If I follow the sample from this link, I can accomplish it without a problem by using the UI in the Azure Portal, but I can't find any documentation on Microsoft Docs to do it using PowerShell.
Write-Host "Creating Function App..."
$fnApp = New-AzFunctionApp -Name $functionAppName `
-ResourceGroupName $emailFunctionRg `
-Location "$(AzureRegion)" `
-StorageAccount $storageName `
-Runtime dotnet `
-FunctionsVersion '3' `
-IdentityType SystemAssigned
Write-Host "Function App created!"
Write-Host "Assigning Key Vault access..."
$appId = Get-AzADServicePrincipal -DisplayName $functionAppName
Set-AzKeyVaultAccessPolicy -VaultName EmailSettings -ServicePrincipalName $appId -PermissionsToSecrets Get,List
Write-Host "Key Vault access granted!"
Running Set-AzKeyVaultAccessPolicy fails with "Insufficient privileges to complete the operation.". But I am not sure if this is the right path to follow, it was just a guess, based on the available functions in the documentation.
Any ideas?

Two potential issues to check out here:
your app creation assigns the result to $fnApp. perhaps $fnApp or as commented above, $fnApp.ApplicationId is what you should be using for the -ServicePrincipalName parameter on the access policy grant.
you don't have privileges to assign RBAC roles. Go to the Key Vault, choose Access Control, then click the Role Assignments tab and verify that your user appears in the list as an Administrator, User Access Administrator, or Owner.
Edit: With respect to the RBAC privilege, since this is running in Azure Powershell from Azure DevOps, you need to check the role assignment for the Service Connection's service principal - under Azure Active Directory in the Azure Portal, look up the principal used to create the service connection, and make sure THAT gets the correct Role on the key vault.

After a little of trial and error I just came to the conclusion I was not using the right parameter for the Set-AzKeyVaultAccessPolicy cmdlet.
The following script will work (if the service principle running it has the appropriate role, like WaitingForGuacamole mentioned in his/her answer):
Write-Host "Creating Function App..."
$fnApp = New-AzFunctionApp -Name <FnAppName> `
-ResourceGroupName <ResourceGroupName> `
-Location <AzureRegion> `
-StorageAccount <StorageAccount> `
-Runtime dotnet `
-FunctionsVersion '3' `
-IdentityType SystemAssigned
Write-Host "Function App created!"
Write-Host "Assigning Key Vault access..."
Set-AzKeyVaultAccessPolicy -VaultName <NameOfTheKeyVault> -ObjectId (Get-AzADServicePrincipal -DisplayName <FnAppName>).Id -PermissionsToSecrets <Get, List, etc...>
Write-Host "Key Vault access granted!"

Related

Add/get secret to Azure Keyvault with Azure powershell task in Azure devops

I am trying to set the secrets inside my Azure Keyvault using the Azure Powershell Task in Azure DevOps.
I use the following code:
Set-AzureKeyVaultSecret -VaultName $KeyvaultName -Name $SecretName -SecretValue $secretvalue
With the names and the value all setup inside variables and tried to use this without also variables.
The value is saved as a secure string with the following code.ConvertTo-SecureString
But when I run this powershell code inside my Azure DevOps Release pipeline I keep getting following Error message:
Cannot retrieve access token for resource 'AzureKeyVaultServiceEndpointResourceId'. Please ensure that you have provided the appropriate access tokens when using access token login.
So I've made sure that the service principal and the build server are having the right access on the keyvault by adding them both to the access policies with the get,list,set secrets permission.
I've also added following lines of code to make sure that the profile is loaded correctly
########################################################################################
$azureRmProfile = [Microsoft.Azure.Commands.Common.Authentication.Abstractions.AzureRmProfileProvider]::Instance.Profile
$profileClient = New-Object -TypeName Microsoft.Azure.Commands.ResourceManager.Common.RMProfileClient -ArgumentList ($azureRmProfile)
$context = Get-AzureRmContext
$AzureToken = $profileClient.AcquireAccessToken($context.Tenant.Id)
Add-AzureRmAccount -AccessToken $AzureToken.AccessToken -AccountId $AzureToken.UserId
########################################################################################
By adding this code in the beginning of the inline script and using the profile with the commando as variable to the -DefaultProfile.
I also enabled the option to enable the script to access the Oauth token.
Is there someone who also tried to set the secret from the powershell task in Azure DevOps. Or know why the powershell script can't get access on the keyvault.
The azurermContext commando provided me with the right output, even tried the Get-AzureRmKeyvault command to figure out if the connection to the environment was already setup right. And that also didn't gave any problems.
below working for sure (using this reguarly)
Set-AzContext -SubscriptionId $SubscriptionId
## $SubscriptionId is a subscription ID where is the target KV
$Secretvalue = ConvertTo-SecureString $SecretValuePlainText -AsPlainText -Force
## $SecretValuePlainText is the secret to store
Set-AzKeyVaultSecret -VaultName $KeyVaultName -Name $SecretName -SecretValue $Secretvalue -ErrorVariable setSecretError -Expires $ExpirationDate -NotBefore $ActivationDate
## $SecretName, $ExpirationDate, $ActivationDate - obvious :)
of course if your refer to variable not from script or inline, but from release the use $(variable_name)
Service Principal/Service Connection we use for this is temporary an Owner of target subscription (or key vault, up to you).
I had the exact same issue.
Found that the problem was a missing access token.
Namely -KeyVaultAccessToken when you call Add-AzureRmAccount.
Found the solution here: https://github.com/Azure/azure-powershell/issues/4818#issuecomment-376155173
I fixed my question with the following.
I used a service connection that was based on a managed identity. And this needed some workaround to access the key vault like #john mentioned. But this was unnecessary. by creating a new service connection based on a service principal. This workaround was not necessary and fixed the issue.

Azure storage account key updation using RM module

I am trying to setup a powershell code which would update the storage account credentials every once in a while and below is the script that I have come across and it works perfectly fine.
function setupContext(){
Add-AzureRmAccount
Save-AzureRmContext -Path “path\to\json\file”
}
#setupContext
Import-AzureRmContext -Path “path\to\json\file”
$subscriptionId='***********************************'
Select-AzureRMSubscription -SubscriptionId $subscriptionId -WarningAction SilentlyContinue
$resourceGroup="**************"
$storageAccountName="******************"
$BLOBKey= New-AzureRmStorageAccountKey -ResourceGroupName $resourceGroup -Name $storageAccountName -KeyName key2
Write-Host "BLOB Key:"$BLOBKey.Keys[0]
The above code does the required work, however it requires us to login to the azure-rm account which basically defeats the idea of automating this process since I would need keep updating this generated profile.
Note: I am not allowed to use az module as of now since the environment in which I work has some .NET version limitations.
So if there any other solution which could overcome the azure rm login issue, please suggest.
Use Azure Automation. This automatically sets up something called RunAs account. Which simply said is just Azure AD Service Principal.
Then assign this principal privileges on the storage account just like any other user and you are done.
And in the Automation Runbook do
$connection = Get-AutomationConnection -Name AzureRunAsConnection
Connect-AzureRmAccount `
-ServicePrincipal `
-Tenant $connection.TenantID `
-ApplicationID $connection.ApplicationID `
-CertificateThumbprint $connection.CertificateThumbprint
$AzureContext = Select-AzureRmSubscription -SubscriptionId $connection.SubscriptionID
... run rest of the code ...
If you want to run this from outside of Azure like on-prem server then set up manually service principal. Here is guide
https://learn.microsoft.com/en-us/azure/active-directory/develop/howto-create-service-principal-portal
And just log into this app from powershell instead of the user.
Looks you want to use a non-interactive way to do that automatically. To access the azure resource with a non-interactive way, your best option currently is to use the service principal(AD App).
An Azure service principal is an identity created for use with applications, hosted services, and automated tools to access Azure resources.
The other reply is for azure automation runbook, you could follow my steps to automate it in other places else.
1.Create an Azure Active Directory application and create a secret for the app, save the secret and get values for signing in.
2.Navigate to the storage account(or the subscription which the storage account located) in the portal -> Access control (IAM) -> Add -> Add role assignment -> search your service principal(AD App) with name and add it as a role(e.g. Owner/Contributor) -> Save.
Note: To give the role, you need to use an account which is an Owner of the specific scope(storage account/subscription).
3.Then use the script as below, replace the specific properties with the values in step 1.
function setupContext(){
$azureAplicationId ="<application id>"
$azureTenantId= "<tenant id>"
$azurePassword = ConvertTo-SecureString "<client secret>" -AsPlainText -Force
$psCred = New-Object System.Management.Automation.PSCredential($azureAplicationId , $azurePassword)
Add-AzureRmAccount -Credential $psCred -TenantId $azureTenantId -ServicePrincipal
Save-AzureRmContext -Path “path\to\json\file”
}
#setupContext
Import-AzureRmContext -Path “path\to\json\file”
$subscriptionId='***********************************'
Select-AzureRMSubscription -SubscriptionId $subscriptionId -WarningAction SilentlyContinue
$resourceGroup="**************"
$storageAccountName="******************"
$BLOBKey= New-AzureRmStorageAccountKey -ResourceGroupName $resourceGroup -Name $storageAccountName -KeyName key2
Write-Host "BLOB Key:"$BLOBKey.Keys[0]
Besides, if you want to learn more about the service principal, you could take a look at this link - Application and service principal objects in Azure Active Directory

Creating KeyVault in Azure with no user

Is there a way in Azure to create keyvault without any user? I am trying to follow the documentation but dont see any command that will achieve this in one line?
New-AzureRmKeyVault -VaultName my-test -ResourceGroupName abc -Location "Brazil South"
Any powershell command I can add above to not create any access policy or principal user?
You can use a template to create a vault with no access policy - see: https://github.com/Azure/azure-quickstart-templates/blob/master/101-key-vault-create/azuredeploy.json and just remove this: https://github.com/Azure/azure-quickstart-templates/blob/master/101-key-vault-create/azuredeploy.json#L105-L114
You can deploy that with PowerShell using New-AzureRMResourceGroupDeployment.
The New-AzureRmKeyVault create keyvault with access policy by default, if you want to achieve this in one line, you could use powershell pipeline, try the command below, it works fine on my side.
New-AzureRmKeyVault -VaultName '<VaultName>' -ResourceGroupName '<ResourceGroupName>' -Location 'East US' | Remove-AzureRmKeyVaultAccessPolicy -ObjectId "<ObjectId of your signin user or service principal>"
Update:
I suppose you create the keyvault via a user account, you could get the ObjectId via Get-AzureRmADUser,-UserPrincipalName is your account.
Complete command:
$ObjectId = (Get-AzureRmADUser -UserPrincipalName "xxxx#xxxx.com").Id
New-AzureRmKeyVault -VaultName '<VaultName>' -ResourceGroupName '<ResourceGroupName>' -Location 'East US' | Remove-AzureRmKeyVaultAccessPolicy -ObjectId $ObjectId

Get-AzureKeyVaultSecret : Operation returned an invalid status code 'Forbidden'

I have code where i'm trying to get the azurevaultsecret and keep that secrete in one variable. while running the code i am getting forbidden error. Please share the valuable solution.
$ssAADKey = ConvertTo-SecureString $AADKey -AsPlainText -Force
$psCredential = New-Object System.Management.Automation.PSCredential($AADAppID, $ssAADKey)
Connect-AzureRmAccount -ServicePrincipal -Credential $psCredential -TenantId $TenantId
$myApp = Get-AzureADApplication -Filter "DisplayName eq '$($AppName)'" -ErrorAction SilentlyContinue
$Secrets = Get-AzureKeyVaultSecret -VaultName "TestVault1" -name "TestSecret1" -ErrorAction Stop
$password =$Secrets.SecretValueText
I test with your code in my site and it works well.
According to your description and error message you provided, I assume that you may not give full permision to your Azure Key Vault. You could refer to the following steps to troubleshoot.
1.Add a new app registration in Azure AD. Then we can get tenantId, appId, secretKey from the Azure Portal, please refer to this article.
2.Add permission with "Key Vault" to the registered app.
3.In Key vault channel, you need to Add policies to your registered application or user. And in Access Control you need to add permission to your registered application or user.
For more details, you could refer to this SO thread.
One more recent cause of the 'Forbidden' error is that you've enabled the Firewalls and virtual networks feature, and haven't enabled the "Allow trusted Microsoft services to bypass this firewall?" option which can be found here:
Log into the Azure Portal
Navigate to your Key Vault
From Settings, select Firewalls and virtual networks
Scroll down to the section entitled Exception
Make sure your Azure CLI /Client public IP is allowed Key Vault Network Firewall access to the key vault in question (Azure Key Vault; Networking; Firewall; IPv4 address or CIDR) in addition to having permission to update/modifying the key vault.
Determine your CLI public IP by use:
(Invoke-WebRequest -Uri https://myexternalip.com/raw -UseBasicParsing).Content

Azure KeyVault: SPN KeyVault Access Denied

I am using below Azure Powershell command in VSTS.
(Get-AzureKeyVaultSecret -vaultName "debugkv" -name "CoreConfig-StorageAccount-AccessKey")
I am getting ##[error]Access denied error while running it in VSTS but loclaly it works fine.
I have added the SPN in KV's access policies also with GET and SET permissions for secrets.
Need help in troubleshooting it.
To link VSTS to you need to give the Service Principal, which forms the Service Endpoint in VSTS, access to the Key Vault; you already know this.
What can be confusing is that you can assign the application and the service principal to have access to the key vault depending on your use case. Therefore, you must ensure that you assign the right object to the access policy.
The best way to ensure you assign the right object is to do it through Azure Powershell.
Running a signed in Azure Powershell session:
$spObjectId = Get-AzureRmAdServicePrincipal -SearchString <ServicePrincipalName> | Foreach-Object {$_.Id}
Set-AzureRmKeyVaultAccessPolicy -VaultName <VaultName> -ObjectId $spObjectId -PermissionsToSecrets Get,Set
If you wanted to see further details (objectids, permissions etc) of the access policies you can get these through Powershell also:
Get-AzureRmKeyVault -VaultName <VaultName> | Foreach-Object {$_.AccessPolicies}

Resources