I am deploying a function app via ARM template. I have added the following snippet to add the certificate from the Azure Key vault and the added a host name binding.
{
"type": "Microsoft.Web/certificates",
"apiVersion": "2019-08-01",
"name": "[parameters('certificateName')]",
"location": "North Europe",
"dependsOn": [
"[resourceId('Microsoft.Web/sites', parameters('functionAppName'))]"
],
"properties": {
"keyVaultId": "[resourceId(parameters('keyvaultRG'), 'Microsoft.KeyVault/vaults', parameters('keyvaultName'))]",
"keyVaultSecretName": "[parameters('existingKeyVaultSecretName')]",
"serverFarmId": "[resourceId('Microsoft.Web/serverFarms', parameters('hostingPlanName'))]"
}
},
{
"type": "Microsoft.Web/sites/hostnameBindings",
"name": "[concat(parameters('functionAppName'), '/', parameters('customDomainName'))]",
"apiVersion": "2019-08-01",
"location": "North Europe",
"dependsOn": [
"[resourceId('Microsoft.Web/certificates', parameters('certificateName'))]"
],
"properties": {
"sslState": "SniEnabled",
"thumbprint": "[reference(resourceId('Microsoft.Web/certificates', parameters('certificateName'))).Thumbprint]"
}
}
But when I don deploy the ARM template I get the following error at Microsoft.Web/certificates
"message": "The parameter httpResponseMessage has an invalid value."
Please check if the below steps help to fix the issue:
error at Microsoft.Web/certificates "message": "The parameter {0} has an invalid value."
Follow the below steps to fix this issue:
Enable the Microsoft.Web resource provider to direct access the Azure Key Vault using the Azure PowerShell.
Login-AzureRmAccount
Set-AzureRmContext -SubscriptionId AZURE_SUBSCRIPTION_ID
Set-AzureRmKeyVaultAccessPolicy -VaultName KEY_VAULT_NAME -ServicePrincipalName <ServicePrincipalId> -PermissionsToSecrets get
Check the Access Policies of Azure Key Vault: Azure Key Vault > Access Policies > Add New (Microsft.Web) Your-Function-App
Azure PowerShell Commands for inserting the certificate to the KeyVault:
$pfxFilePath = "PFX_CERTIFICATE_FILE_PATH" # Change this path
$pwd = "PFX_CERTIFICATE_PASSWORD" # Change this password
$flag = [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::Exportable
$collection = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2Collection
$collection.Import($pfxFilePath, $pwd, $flag)
$pkcs12ContentType = [System.Security.Cryptography.X509Certificates.X509ContentType]::Pkcs12
$clearBytes = $collection.Export($pkcs12ContentType)
$fileContentEncoded = [System.Convert]::ToBase64String($clearBytes)
$secret = ConvertTo-SecureString -String $fileContentEncoded -AsPlainText –Force
$secretContentType = 'application/x-pkcs12'
Set-AzureKeyVaultSecret -VaultName KEY_VAULT_NAME -Name KEY_VAULT_SECRET_NAME -SecretValue $Secret -ContentType $secretContentType # Change Name of Azure KV & Secret
Next step is using the KeyVaultSecretName for directly accessing the KeyVault in order to get the value.
WebSite.parameters:
{
"$schema": "https://schema.management.azure.com/schemas/2019-08-01/deploymentParameters.json",
"contentVersion": "1.0.0.0",
"parameters": {
"functionAppName": {
"value": "yourfunctionappname"
},
"customHostname": {
"value": "yourcustomdomianname"
},
"existingKeyVaultId": {
"value": "/subscriptions/subscriptionsID/resourceGroups/resourceGroupsName/providers/Microsoft.KeyVault/vaults/vaultsName"
},
"existingKeyVaultSecretName": {
"value": "The key vaults SecretName"
}
}
}
Related
I am trying to create a private link to a Microsoft partner service in Azure using Powershell.
When I configure the endpoint through the Azure console, the segment of the template for the endpoint looks as follows:
{
"type": "Microsoft.Network/privateEndpoints",
"apiVersion": "2020-11-01",
"name": "[parameters('privateEndpoints_foo_pl_silverfish_name')]",
"location": "eastus2",
"dependsOn": [
"[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworks_foo_pl_vnet_name'), 'foo_pl_subnet')]"
],
"tags": {
"owner": "foo"
},
"properties": {
"privateLinkServiceConnections": [],
"manualPrivateLinkServiceConnections": [
{
"name": "[parameters('privateEndpoints_foo_pl_silverfish_name')]",
"properties": {
"privateLinkServiceId": "xyz-prod.67395a8a-a9d4-4c85-bd01-109a99e7eca2.eastus2.azure.privatelinkservice",
"groupIds": [],
"privateLinkServiceConnectionState": {
"status": "Approved"
}
}
}
],
"subnet": {
"id": "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworks_foo_pl_vnet_name'), 'foo_pl_subnet')]"
},
"customDnsConfigs": []
}
}
You can see the privateLinkServiceConnections array is empty. I've got content under the manualPrivateLinkServiceConnections array, which doesn't conform to the options in the New-AzPrivateLinkServiceConnection commandlet.
This is with me creating the private endpoint through the console:
Can I create a manual private link service connection with the New-AzPrivateLinkServiceConnection and New-AzPrivateEndpoint commandlets?
## Create the private link service configuration
$plink = New-AzPrivateLinkServiceConnection `
-Name 'foo_pl_silverfish_config' `
-PrivateLinkServiceId 'xyz-prod.67395a8a-a9d4-4c85-bd01-109a99e7eca2.eastus2.azure.privatelinkservice'
$privateEndpoint = New-AzPrivateEndpoint `
-ResourceGroupName 'foo_private_link' `
-Name 'foo_pl_db' `
-Location 'eastus2' `
-Subnet $sub `
-PrivateLinkServiceConnection $plink
I get the following error when running the above:
New-AzPrivateEndpoint: Operation returned an invalid status code 'BadRequest'
StatusCode: 400
ReasonPhrase: Bad Request
ErrorCode: LinkedInvalidPropertyId
ErrorMessage: Property id 'xyz-prod.67395a8a-a9d4-4c85-bd01-109a99e7eca2.eastus2.azure.privatelinkservice' at path 'properties.privateLinkServiceConnections[0].properties.privateLinkServiceId' is invalid. Expect fully qualified resource Id that start with '/subscriptions/{subscriptionId}' or '/providers/{resourceProviderNamespace}/'.
ErrorMessage: Property id
'xyz-prod.67395a8a-a9d4-4c85-bd01-109a99e7eca2.eastus2.azure.privatelinkservice'
at path
'properties.privateLinkServiceConnections[0].properties.privateLinkServiceId'
is invalid. Expect fully qualified resource Id that start with
'/subscriptions/{subscriptionId}' or
'/providers/{resourceProviderNamespace}/'.
You need to pass the resourceId for the property PrivateLinkServiceId to the New-AzPrivateLinkServiceConnection cmdlet.
Can I create a manual private link service connection with the
New-AzPrivateLinkServiceConnection and New-AzPrivateEndpoint
commandlets?
Yes, you can create manualPrivateLinkServiceConnections using -ByManualRequest in the New-AzPrivateLinkServiceConnection powershell cmdlet.
Here is the reference documentation for more information about the supported properties for the above cmdlet.
I have tested this in my local environment by creating a private endpoint to storage table in one of the storage accounts in my subscription.
Here is the cmdlet that I have used:
$virtualNetwork = Get-AzVirtualNetwork -ResourceName '<VirtualNetworkName>' -ResourceGroupName '<resourceGroupName>'
$subnet = $virtualNetwork | Select-Object -ExpandProperty subnets | Where-Object Name -eq '<SubnetName>'
$plsConnection= New-AzPrivateLinkServiceConnection -Name '<PLSConnectionsName>' -PrivateLinkServiceId '<ResourceID to which resource you want to create endpoint connection>' -RequestMessage 'Please Approve my request' -GroupId 'queue'
New-AzPrivateEndpoint -Name '<PrivateEndpointName>' -ResourceGroupName '<ResourceGroupName>' -Location 'eastus' -PrivateLinkServiceConnection $plsConnection -Subnet $subnet -ByManualRequest
Here is the sample output for your reference:
Updated Answer:
If you want to create the private endpoint using the private link service alias(foo..<azure_region>.azure.privatelinkservice) you can use the below powershell script.
$virtualNetwork = Get-AzVirtualNetwork -ResourceName '<VirtualNetworkName>' -ResourceGroupName '<resourceGroupName>'
$subnet = $virtualNetwork | Select-Object -ExpandProperty subnets | Where-Object Name -eq '<SubnetName>'
$privatelinkAlias= Get-AzPrivateLinkService -Name '<privateServiceLinkName>' -ResourceGroupName '<resourceGroupName>'
$plsConnection= New-AzPrivateLinkServiceConnection -Name '<PLSConnectionsName>' -PrivateLinkServiceId $privatelinkAlias.Alias -RequestMessage 'Please Approve my request'
New-AzPrivateEndpoint -Name '<PrivateEndpointName>' -ResourceGroupName '<ResourceGroupName>' -Location 'eastus' -PrivateLinkServiceConnection $plsConnection -Subnet $subnet -ByManualRequest
Here is the sample output screenshot:
The only way I was able to build the private endpoint was using an ARM template that I deployed via Powershell script.
The ARM template looks like the following:
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"location": {
"type": "string",
"defaultValue": "[resourceGroup().location]",
"metadata": {
"description": "The location for resources created as part of the deployment."
}
},
"virtualNetworkName": {
"type": "string",
"metadata": {
"description": "The name of the virtual network hosting the private endpoint for the Astra private link."
}
},
"privatelinkEndpointSubnetName": {
"type": "string",
"metadata": {
"description": "The name of the subnet hosting the private endpoint."
}
},
"privatelinkEndpointSubnetCIDR": {
"type": "string",
"metadata": {
"description": "The CIDR range associated with the subnet hosting the private endpoint."
}
},
"privateEndpointName": {
"type": "string",
"metadata": {
"description": "The name of the private endpoint associated with the database's private link."
}
},
"AstraDBServiceName": {
"type": "string",
"metadata": {
"description": "The 'Service Name' for the subject Astra DB database."
}
}
},
"variables": {
"PrivatelinkEndpointSubnetNameRef": "[concat(parameters('virtualNetworkName'), '/', parameters('privatelinkEndpointSubnetName'))]",
"PrivatelinkEndpointSubnetIdRef": "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('privatelinkEndpointSubnetName'))]"
},
"resources": [{
"apiVersion": "2018-04-01",
"type": "Microsoft.Network/virtualNetworks/subnets",
"name": "[variables('PrivatelinkEndpointSubnetNameRef')]",
"location": "[parameters('location')]",
"properties": {
"addressPrefix": "[concat(parameters('privatelinkEndpointSubnetCIDR'))]",
"privateLinkServiceNetworkPolicies": "Disabled",
"privateEndpointNetworkPolicies": "Disabled"
}
},
{
"dependsOn": [
"[variables('PrivatelinkEndpointSubnetIdRef')]"
],
"apiVersion": "2019-02-01",
"type": "Microsoft.Network/privateEndpoints",
"name": "[parameters('privateEndpointName')]",
"location": "[parameters('location')]",
"properties": {
"manualPrivateLinkServiceConnections": [{
"name": "{plsConnection}",
"properties": {
"privateLinkServiceId": "[parameters('AstraDBServiceName')]",
"requestMessage": "Please approve my connection, thanks."
}
}],
"subnet": {
"id": "[variables('PrivatelinkEndpointSubnetIdRef')]"
}
}
}
]
}
Here is the code I would use to build the template...
$arm_template_parameters = #{
'AstraDBServiceName' = 'abc.593caf89-071c-4be7-f5ce9d3f9bc6
.eastus.azure.privatelinkservice'
'privateEndpointName' = 'myEndpoint'
'virtualNetworkName' = 'myVNET'
'privatelinkEndpointSubnetName' = 'mySubnet'
'privatelinkEndpointSubnetCIDR' = '10.0.2.0/24'
}
New-AzResourceGroupDeployment `
-Name 'myDeployment' `
-ResourceGroupName 'myResourceGroup' `
-TemplateFile '.\template.json' `
-TemplateParameterObject $arm_template_parameters `
-verbose
The problem for me is I ONLY have a resource id or alias. I can build from an ARM template or the Azure console. All the examples building the PE using Powershell require me to know the details about the service's load balancer, which I don't.
I have extracted the ARM template belonging to the preview version of Azure App Configuration, and am setting it into our IaC repository - so far so good.
Our next logical step is to include insertion of the AppConfiguration.PrimaryKey into our Key Vault. However I do not know the name of this property, and I can not find any information on the subject online. Also I can not see the AppConfiguration/configurationStores type listed in resources.azure.com (assuming its because its still in public preview).
Does anyone know how to reference the primary key (and possibly the read-only primary key), so i can reference them through a "outputs" variable in my arm template?
Then I can let Az Cli/Az Powershell insert the secret into our Key Vault, and we obtain full automation of our IaC
I was not able to figure this out.
However by using az cli commands in a IaC script (which anyways invokes the arm template residing in a azure blob store) I circumvented the problem:
$connStrings = az appconfig credential list -n $configName| ConvertFrom-Json
$readOnlyConnString = ($connStrings | Where {$_.name -eq "Primary Read Only"}).connectionString
$primaryConnString = ($connStrings | Where {$_.name -eq "Primary"}).connectionString
#then
az keyvault secret set --vault-name $kvName --name $keyNameRO --value $readOnlyConnString
az keyvault secret set --vault-name $kvName --name $keyNamePrimary --value $primaryConnString
For an ARM template I did the following. The listkeys function returns a full list of all the values that have to do with the keys. This was hard to figure out. I hope it helps.
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"configurationStores_instance_name": {
"defaultValue": "ac-instance",
"type": "String"
}
},
"variables": {
"apiVersionVar": "[providers('Microsoft.AppConfiguration', 'configurationStores').apiVersions[0]]",
"resourceId": "[resourceId('Microsoft.AppConfiguration/configurationStores', parameters('configurationStores_instance_name'))]",
},
"resources": [
{
"type": "Microsoft.AppConfiguration/configurationStores",
"apiVersion": "2019-10-01",
"name": "[parameters('configurationStores_instance_name')]",
"location": "northcentralus",
"sku": {
"name": "standard"
},
"properties": {}
}
],
"outputs": {
"AppConfigEndpoint": {
"type": "string",
"value": "[reference(parameters('configurationStores_instance_name')).endpoint]"
},
"AppConfigKeys": {
"type": "Array",
"value": "[listkeys(variables('resourceId'), variables('apiVersionVar')).value]"
}
}
}
hope this helps!
I've searched online and browsed the available powershell cmdlets to try and find a solution for this problem but have been unsuccessful. Essentially, I have a few Data Factory pipelines that copy/archive incoming files and will use a web http post component that will invoke a Logic App that connects to a Blob container and will delete the incoming file. The issue I'm facing is that we have several automation runbooks that will rest Blob access keys every X days. When the Blob keys get reset the Logic App will fail whenever this happens because the connection is manually created in the designer itself and I can't specify a connection string that could pull from the Keyvault, as an example. Inside of the {Logic App > API Connections > Edit API Connection} we can manually update the connection string/key but obviously for an automated process we should be able to do this programmatically.
Is there a powershell cmdlet or other method I'm not seeing that would allow me to update/edit the API Connections that get created when using and Blob component inside a Logic App?
Any insights is appreciated!
Once you've rotated your key in the storage account, you can use an ARM template to update your connection API. In this ARM template, the connection api is created referencing the storage account internally so you don't have to provide the key:
azuredeploy.json file:
{
"$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"azureBlobConnectionAPIName": {
"type": "string",
"metadata": {
"description": "The name of the connection api to access the azure blob storage."
}
},
"storageAccountName": {
"type": "string",
"metadata": {
"description": "The Storage Account Name."
}
}
},
"variables": {},
"resources": [
{
"type": "Microsoft.Web/connections",
"name": "[parameters('azureBlobConnectionAPIName')]",
"apiVersion": "2016-06-01",
"location": "[resourceGroup().location]",
"scale": null,
"properties": {
"displayName": "[parameters('azureBlobConnectionAPIName')]",
"parameterValues": {
"accountName": "[parameters('storageAccountName')]",
"accessKey": "[listKeys(resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName')),'2015-05-01-preview').key1]"
},
"api": {
"id": "[concat('subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Web/locations/', resourceGroup().location, '/managedApis/azureblob')]"
}
},
"dependsOn": []
}
]
}
azuredeploy.parameters.json file:
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"azureBlobConnectionAPIName": {
"value": "myblobConnectionApiName"
},
"storageAccountName": {
"value": "myStorageAccountName"
}
}
}
You can them execute the arm template like that:
Connect-AzureRmAccount
Select-AzureRmSubscription -SubscriptionName <yourSubscriptionName>
New-AzureRmResourceGroupDeployment -Name "ExampleDeployment" -ResourceGroupName "MyResourceGroupName" `
-TemplateFile "D:\Azure\Templates\azuredeploy.json" `
-TemplateParameterFile "D:\Azure\Templates\azuredeploy.parameters.json"
to get started with ARM template and powerhsell, you cam have a look at this article:
Deploy resources with Resource Manager templates and Azure PowerShell
I am using the following JSON input file to create a new notification hub in PowerShell:
"Name": "my-tester",
"Location": "West US",
"Tags": {
"tag1": "tag"
},
"Properties": {
"ApnsCredential": {
"Properties": {
"KeyId": "xxx",
"Token": "xxx",
"AppName": "xxx",
"AppId": "xxx",
"Endpoint":"https://api.push.apple.com:443/3/device"
}
},
"GcmCredential": {
"Properties": {
"GoogleApiKey": "yyyy"
}
}
}
}
The PowerShell command is:
$newHub = New-AzureRmNotificationHub -Namespace $nameSpace -ResourceGroup $resourceGroup -InputFile ".\$newHubName-HubProps.json"
The command does create the hub ...but the PNS credentials are empty (I also verified from the portal). So when I inspect the
newly created hub using this PowerShell, I get nothing. Here is the PowerShell command:
$newHubPnsCreds = Get-AzureRmNotificationHubPNSCredentials -Namespace $nameSpace -ResourceGroup $resourceGroup -NotificationHub $newHubName
# This returns nothing:
$newHubPnsCreds.GcmCredential
Any help is appreciated.
Regards
I am trying to create a master key vault, which will contain all certificates to authenticate as a certain user.
I have 2 service principals => One for my app, One for deployment.
The idea is that the deploy service principal gets access to the Key Vault and adds the certificate located there to the Store of the web applications.
I have created the service principal and I have given him all permissions on the key vault. Also I have enabled access secrets in ARM templates for that key vault.
Using powershell I am able to login as the Deploying SP and retrieving the secret (certificate).
However this does not work when deploying the ARM template with a reference to the key vault. I got the following error:
New-AzureRmResourceGroupDeployment : 11:16:44 - Resource Microsoft.Web/certificates 'test-certificate' failed with message '{
"Code": "BadRequest",
"Message": "The service does not have access to '/subscriptions/98f06e7e-1016-4088-843f-62690f3bb306/resourcegroups/rg-temp/providers/microsoft.keyvault/vaults/master-key-vault' Key
Vault. Please make sure that you have granted necessary permissions to the service to perform the request operation.",
"Target": null,
"Details": [
{
"Message": "The service does not have access to '/subscriptions/xxxx/resourcegroups/xxx/providers/microsoft.keyvault/vaults/master-key-vault' Key
Vault. Please make sure that you have granted necessary permissions to the service to perform the request operation."
},
My ARM template looks like this:
{
"type":"Microsoft.Web/certificates",
"name":"test-certificate",
"apiVersion":"2016-03-01",
"location":"[resourceGroup().location]",
"properties":{
"keyVaultId":"[resourceId('rg-temp', 'Microsoft.KeyVault/vaults', 'master-key-vault')]",
"keyVaultSecretName":"kv-certificate-test",
"serverFarmId":"[resourceId('Microsoft.Web/serverfarms', 'asp-test')]"
}
},
Is this a bug? Because I am able to retrieve the certificate using the Deploy SP with:
$key = Get-AzureKeyVaultSecret -VaultName "master-key-vault" -Name "testenvironmentcertificate"
This is my ARM template: (note, the Key vault lives in another resource group than the resources in the ARM template)
{
"$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json",
"contentVersion": "1.0.0.0",
"parameters": {},
"variables": {},
"resources": [
{
"type":"Microsoft.Web/certificates",
"name":"test-certificate",
"apiVersion":"2016-03-01",
"location":"[resourceGroup().location]",
"properties":{
"keyVaultId":"/subscriptions/xxx/resourceGroups/rg-temp/providers/Microsoft.KeyVault/vaults/xxx",
"keyVaultSecretName":"testcert",
"serverFarmId":"[resourceId('Microsoft.Web/serverfarms', 'asp-test')]"
}
},
{
"name": "wa-test1",
"type": "Microsoft.Web/sites",
"location": "[resourceGroup().location]",
"apiVersion": "2016-08-01",
"dependsOn": [
"[concat('Microsoft.Web/serverfarms/', 'asp-test')]"
],
"tags": {
"[concat('hidden-related:', resourceGroup().id, '/providers/Microsoft.Web/serverfarms/asp-test')]": "Resource",
"displayName": "wa-test1"
},
"properties": {
"name": "wa-test1",
"serverFarmId": "[resourceId('Microsoft.Web/serverfarms', 'asp-test')]"
}
},
{
"name": "asp-test",
"type": "Microsoft.Web/serverfarms",
"location": "[resourceGroup().location]",
"apiVersion": "2014-06-01",
"dependsOn": [],
"tags": {
"displayName": "appServicePlan"
},
"properties": {
"name": "asp-test",
"sku": "Free",
"workerSize": "Small",
"numberOfWorkers": 1
}
}
]
}
I believe you are missing a permission for a Resource Provider to access Key Vault, so the WebApp is using its own Resource Provider to do that, you need to grant that RP access to key vault:
Set-AzureRmKeyVaultAccessPolicy -VaultName KEYVAULTNAME -PermissionsToSecrets get `
-ServicePrincipalName abfa0a7c-a6b6-4736-8310-5855508787cd
Reference:
https://azure.github.io/AppService/2016/05/24/Deploying-Azure-Web-App-Certificate-through-Key-Vault.html
I tried all the answers but they didn't work. Here's what worked for me:
setting the access permissions for the two service principals on the key vault:
Read more here:
https://devsdaily.com/key-vault-failed-to-sync-the-certificate-the-service-does-not-have-access-to-key-vault/
I was not able to add the policies through the Set-AzureRmKeyVaultAccessPolicy command due to an error in the console.
I was however able to resolve the issue through the Azure Web Interface by opening the KeyVault Access Control(IAM) and adding Key Vault Reader and Key Vault Secrets User roles to Microsoft.Azure.Websites
I wrote the same answer here as well.