Add multiple access policy in Key Vault for different resource group object id - azure-keyvault

I am trying to add multiple access policy in key vault for 2 object ids. One of them which belongs to same resource group as key vault and another belongs to a different resource group.
"resources": [
{
"type": "Microsoft.KeyVault/vaults",
"apiVersion": "2021-11-01-preview",
"name": "[parameters('vaultName')]",
"location": "[resourcegroup().location]",
"properties": {
"sku": {
"family": "A",
"name": "standard"
},
"tenantId": "[subscription().tenantId]",
"accessPolicies": [
{
"tenantId": "[reference(concat('Microsoft.Web/sites/', variables('functionName'), '/providers/Microsoft.ManagedIdentity/Identities/default'), '2015-08-31-PREVIEW').tenantId]",
"objectId": "[reference(concat('Microsoft.Web/sites/', variables('functionName'), '/providers/Microsoft.ManagedIdentity/Identities/default'), '2015-08-31-PREVIEW').principalId]",
"permissions": {
"secrets": [
"get",
"list"
]
}
},
{
"tenantId": "[reference(concat('Microsoft.Web/sites/', variables('functionNameAnotherRg'), '/providers/Microsoft.ManagedIdentity/Identities/default'), '2015-08-31-PREVIEW').tenantId]",
"objectId": "[reference(concat('Microsoft.Web/sites/', variables('functionNameAnotherRg'), '/providers/Microsoft.ManagedIdentity/Identities/default'), '2015-08-31-PREVIEW').principalId]",
"permissions": {
"secrets": [
"get",
"list"
]
}
}
],
"enabledForDeployment": false,
"enabledForDiskEncryption": false,
"enabledForTemplateDeployment": true,
"enableSoftDelete": true,
"softDeleteRetentionInDays": 90
}
}
]
I am getting error as application does not found in resource group. Can someone please help

Found the solution. We need to specify resource group name in order to grant access to another application belongs to another resource group.
{
"tenantId": "[reference(resourceId(parameters('AnotherResourceGroup'),'Microsoft.Web/sites', parameters('FunName')), '2021-03-01', 'Full').identity.tenantId]",
"objectId": "[reference(resourceId(parameters('AnotherResourceGroup'),'Microsoft.Web/sites', parameters('FunName')), '2021-03-01', 'Full').identity.principalId]",
"permissions": { "secrets": [ "Get", "list" ] }
}

Related

ARM KeyVault Access Policies Conditional Add

Is it possible to add an access policy via a conditional statement? Basically, if environment == production I don't want to add the registration.
I have the following in my template however I don't want the application called foobarApplicationId to be added if the environment is production. Can I do this inline or do I need a seperate template? Will setting foobarApplicationId to be an empty string work?
{
"name": "[variables('keyVault-name')]",
"type": "Microsoft.KeyVault/vaults",
"apiVersion": "2016-10-01",
"location": "[resourceGroup().location]",
"properties": {
"tenantId": "[subscription().tenantId]",
"sku": {
"family": "A",
"name": "standard"
},
"accessPolicies": [
{
"tenantId": "[subscription().tenantId]",
"objectId": "[parameters('keyVaultOwner')]",
"permissions": {
"keys": [
"all"
],
"secrets": [
"all"
],
"certificates": [
"all"
],
"storage": [
]
}
},
{
"tenantId": "[subscription().tenantId]",
"objectId": "[parameters('foobarApplicationId')]",
"permissions": {
"keys": [
"get",
"wrapKey",
"unwrapKey",
"sign",
"verify",
"list"
],
"secrets": [
"get",
"list"
],
"certificates": [
"get",
"list"
],
"storage": [
]
}
},
"condition" within "accessPolicies" doesn't seem to have any effect for me. It doesn't cause any validation or deployment error, but the access policies got added even when the condition evaluated to false.
I found the following trick works better: Use an if clause for your "objectId" and "permissions", such that if the condition is false, you would assign an empty set of permissions to the empty GUID, effectively becoming a no-op.
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"variables": {
"keyVaultNoPermissions": { },
"keyVaultAppReadPermissions": {
"keys": [ "get", "wrapKey", "unwrapKey", "sign", "verify", "list" ],
"secrets": [ "get", "list" ],
"certificates": [ "get", "list" ]
}
},
"resources": [
// ...
{
"type": "Microsoft.KeyVault/vaults/accessPolicies",
"apiVersion": "2016-10-01",
"name": "[concat(parameters('keyVaultName'), '/add')]",
"location": "[resourceGroup().location]",
"dependsOn": [
"[parameters('keyVaultName')]"
],
"properties": {
"accessPolicies": [
{
"tenantId": "[subscription().tenantId]",
"objectId": "[if(not(equals(parameters('environment'), 'PROD')), parameters('foobarApplicationId'), '00000000-0000-0000-0000-000000000000')]",
"permissions": "[if(not(equals(parameters('environment'), 'PROD')), variables('keyVaultAppReadPermissions'), variables('keyVaultNoPermissions'))]"
}
]
}
}
]
}
It would be in the individual access policy adding a condition section that will take an environment parameter like:
{
"condition": "[not(equals(parameters('environment'),'PROD'))]"
"tenantId": "[subscription().tenantId]",
"objectId": "[parameters('foobarApplicationId')]",
"permissions": {
"keys": [
"get",
"wrapKey",
"unwrapKey",
"sign",
"verify",
"list"
],
"secrets": [
"get",
"list"
],
"certificates": [
"get",
"list"
],
"storage": [
]
}
}
Add the access policies separately, conditionally. You can see an explanation here.
{
"resources": [{
"name": "[variables('keyVault-name')]",
"type": "Microsoft.KeyVault/vaults",
"apiVersion": "2016-10-01",
"location": "[resourceGroup().location]",
"properties": {
"tenantId": "[subscription().tenantId]",
"sku": {
"family": "A",
"name": "standard"
},
"accessPolicies": [{
"tenantId": "[subscription().tenantId]",
"objectId": "[parameters('keyVaultOwner')]",
"permissions": {
"keys": [
"all"
],
"secrets": [
"all"
],
"certificates": [
"all"
],
"storage": [
]
}
}
]
}
}, {
"name": "[concat(variables('keyVault-name'), '/add')]",
"type": "Microsoft.KeyVault/vaults",
"apiVersion": "2016-10-01",
"condition": "[not(startsWith(parameters('environmentName'), 'PROD'))]",
"location": "[resourceGroup().location]",
"properties": {
"tenantId": "[subscription().tenantId]",
"sku": {
"family": "A",
"name": "standard"
},
"accessPolicies": [{
"tenantId": "[subscription().tenantId]",
"objectId": "[parameters('foobarApplicationId')]",
"permissions": {
"keys": [
"get",
"wrapKey",
"unwrapKey",
"sign",
"verify",
"list"
],
"secrets": [
"get",
"list"
],
"certificates": [
"get",
"list"
],
"storage": [
]
}
}
]
}
}
]
}

ARM Template for Importing Azure Key Vault Certificate in Function App

I have a function app which calls another API with a certificate. This certificate (.pfx) file is already present in the key vault. I am using below ARM template to import the certificate to SSL settings of the function app.
Note: the function app gets deployed fine when I remove section "hostNameSslStates". But after adding it, I get -
"Code": "Conflict",
"Message": "The certificate with thumbprint 'XXXXXXXX' does not match the hostname
'blobcreate-eventgridtrigger-functionapp.azurewebsites.net'."
ARM Template resources section-
`
"resources": [
//StorageAccount
{
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2019-06-01",
"name": "[parameters('storageAccounts_name')]",
"location": "[resourceGroup().location]",
"sku": {
"name": "[parameters('storageSKU')]",
"tier": "Standard"
},
"kind": "StorageV2",
"properties": {
"networkAcls": {
"bypass": "AzureServices",
"virtualNetworkRules": [],
"ipRules": [],
"defaultAction": "Allow"
},
"supportsHttpsTrafficOnly": true,
"encryption": {
"services": {
"file": {
"keyType": "Account",
"enabled": true
},
"blob": {
"keyType": "Account",
"enabled": true
}
},
"keySource": "Microsoft.Storage"
},
"accessTier": "Hot"
}
},
//BlobService
{
"type": "Microsoft.Storage/storageAccounts/blobServices",
"apiVersion": "2019-06-01",
"name": "[variables('blobServiceName')]",
"dependsOn": ["[variables('storageAccountResourceId')]"],
"sku": {
"name": "[parameters('storageSKU')]"//,
// "tier": "Standard"
},
"properties": {
"cors": {
"corsRules": []
},
"deleteRetentionPolicy": {
"enabled": false
}
}
},
//function app with server farm
//cert store access policies update-
{
"type": "Microsoft.KeyVault/vaults",
"name": "testARMTemplateKeyVault",
"apiVersion": "2016-10-01",
"location": "[resourceGroup().location]",
"properties": {
"sku": {
"family": "A",
"name": "standard"
},
"tenantId": "c29678d0-eceb-4df2-a225-79cf795a6b64",
"accessPolicies": [
{
"tenantId": "tenantIdOfSubscription", //obtained from Get-AzTenant
"objectId": "objectid of Microsoft Azure App Service", //obtained from Get-AzADServicePrincipal
"permissions": {
"keys": [
"Get",
"List",
"Update",
"Create",
"Import",
"Delete",
"Recover",
"Backup",
"Restore"
],
"secrets": [
"Get",
"List",
"Set",
"Delete",
"Recover",
"Backup",
"Restore"
],
"certificates": [
"Get",
"List",
"Update",
"Create",
"Import",
"Delete",
"Recover",
"ManageContacts",
"ManageIssuers",
"GetIssuers",
"ListIssuers",
"DeleteIssuers"
],
"storage": []
}
}
],
"enabledForDeployment": false,
"enabledForDiskEncryption": false,
"enabledForTemplateDeployment": true,
"enableSoftDelete": true
}
},
{
"type": "Microsoft.Web/serverfarms",
"apiVersion": "2018-02-01",
"name": "[variables('azurefunction_hostingPlanName')]",
"location": "[resourceGroup().location]",
"sku": {
"name": "Y1",
"tier": "Dynamic"
},
"properties": {
"name": "[variables('azurefunction_hostingPlanName')]",
"computeMode": "Dynamic"
}
},
{
"type": "Microsoft.Web/certificates",
"name": "testingcert",
"apiVersion": "2016-03-01",
"location": "[resourceGroup().location]",
"properties": {
"keyVaultId": "[resourceId('Microsoft.KeyVault/vaults', 'testARMTemplateKeyVault')]",
"keyVaultSecretName": "testingcert",
"serverFarmId": "[resourceId('Microsoft.Web/serverfarms', variables('azurefunction_hostingPlanName'))]"
}
},
{
"apiVersion": "2018-11-01",
"type": "Microsoft.Web/sites",
"name": "[parameters('functionAppName')]",
"location": "[resourceGroup().location]",
"kind": "functionapp",
"dependsOn": [
"[variables('azureFunction_serverFarmResourceId')]",
"[variables('storageAccountResourceId')]",
"[resourceId('Microsoft.Web/certificates', 'testingcert')]"
],
"properties": {
"serverFarmId": "[variables('azureFunction_serverFarmResourceId')]",
"siteConfig": {
"appSettings": [
{
"name": "AzureWebJobsStorage",
"value": "[concat('DefaultEndpointsProtocol=https;AccountName=', parameters('storageAccounts_name'), ';AccountKey=', listKeys(variables('storageAccountResourceId'),variables('storageAccountApiVersion')).keys[0].value)]"
},
{
"name": "WEBSITE_CONTENTAZUREFILECONNECTIONSTRING",
"value": "[concat('DefaultEndpointsProtocol=https;AccountName=', parameters('storageAccounts_name'), ';AccountKey=', listKeys(variables('storageAccountResourceId'),variables('storageAccountApiVersion')).keys[0].value)]"
},
{
"name": "WEBSITE_CONTENTSHARE",
"value": "[toLower(parameters('functionAppName'))]"
},
{
"name": "FUNCTIONS_EXTENSION_VERSION",
"value": "~2"
},
{
"name": "WEBSITE_NODE_DEFAULT_VERSION",
"value": "~10"
},
{
"name": "APPINSIGHTS_INSTRUMENTATIONKEY",
"value": "[reference(resourceId('microsoft.insights/components/', parameters('functionApp_applicationInsightsName')), '2015-05-01').InstrumentationKey]"
},
{
"name": "FUNCTIONS_WORKER_RUNTIME",
"value": "dotnet"
},
{
"name": "WEBSITE_LOAD_CERTIFICATES",
"value": "required certificate thumprint"
}
]
},
"hostNameSslStates": [
{
"name": "blobcreate-eventgridtrigger-functionapp.azurewebsites.net",//obtained from custom domains flatform features of the function app
"sslState": "SniEnabled",
"thumbprint": "[reference(resourceId('Microsoft.Web/certificates', 'testingcert')).Thumbprint]",
"toUpdate": true
}
]
}
}
]`
add certificates section in template -
{
"type": "Microsoft.Web/certificates",
"name": "[parameters('CertificateName')]",
"apiVersion": "2019-08-01",
"location": "[resourceGroup().location]",
"dependsOn": [
"[concat('Microsoft.Web/serverFarms/', variables('azurefunction_hostingPlanName'))]"
],
"properties": {
"keyVaultId": "[parameters('keyvaultResourceId')]",
"keyVaultSecretName": "[parameters('invoiceApiCertificateKeyVaultSecretName')]",
"serverFarmId": "[resourceId('Microsoft.Web/serverfarms', variables('azurefunction_hostingPlanName'))]"
}
}
and then add dependsOn for this certificate in the function app-
[resourceId('Microsoft.Web/certificates', parameters('CertificateName'))]
well, the error is quite obvious, you are trying to add a certificate for blobcreate-eventgridtrigger-functionapp.azurewebsites.net but the dns name on the certificate doesnt match that, hence the error. that is probably not the right way to add a certificate unless its going to be used for SSL termination

How to update a Secret in Azure Key Vault only if changed in ARM templates or check if it exists

I have a production keyvault that keeps a reference of secrets that projects can use, but only if deployed using ARM templates such secrets are not handled by people copy pasting them.
When a new project starts, as part of its deployment script, it will create its own keyvault.
I want to be able to run the templates/scripts as part of CI/CD. And this will today result in the same secret having a new version at each run, even though the value did not change.
How to make it only update the keyvault value when the master vault is updated.
In my deployment.sh script I use the following technique.
SendGridUriWithVersion=$((az group deployment create ... assume that the secret exists ... || az group deployment create ... assume that the secret exists ... ) | jq -r '.properties.outputs.secretUriWithVersion.value')
and it works because in the template there is a parameter, if set, that will retrieve the secret and compare it with the new value and only insert if difference. The original problem is that the deployment fails if the secret is not already set (this happens for the first deployment etc).
But then due to Unix ||, the same script is run again without the parameter set and it will use a condition to not try to get the old value and therefore run successful.
Here are the example in dept:
SecretName="Sendgrid"
SourceSecretName="Sendgrid"
SourceVaultName="io-board"
SourceResourceGroup="io-board"
SendGridUriWithVersion=$((az group deployment create -n ${SecretName}-secret -g $(prefix)-$(projectName)-$(projectEnv) --template-uri https://management.dotnetdevops.org/providers/DotNetDevOps.AzureTemplates/templates/KeyVaults/${keyVaultName}/secrets/${SecretName}?sourced=true --parameters sourceVault=${SourceVaultName} sourceResourceGroup=${SourceResourceGroup} sourceSecretName=${SourceSecretName} update=true || az group deployment create -n ${SecretName}-secret -g $(prefix)-$(projectName)-$(projectEnv) --template-uri https://management.dotnetdevops.org/providers/DotNetDevOps.AzureTemplates/templates/KeyVaults/${keyVaultName}/secrets/${SecretName}?sourced=true --parameters sourceVault=${SourceVaultName} sourceResourceGroup=${SourceResourceGroup} sourceSecretName=${SourceSecretName}) | jq -r '.properties.outputs.secretUriWithVersion.value')
The https://management.dotnetdevops.org/providers/DotNetDevOps.AzureTemplates/templates/KeyVaults/{keyvaultName}/secrets/{secretName}?sourced=true returns a template
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"keyVaultName": {
"type": "string",
"defaultValue": "io-board-data-ingest-dev"
},
"secretName": {
"type": "string",
"metadata": {
"description": "Name of the secret to store in the vault"
},
"defaultValue": "DataStorage"
},
"sourceVaultSubscription": {
"type": "string",
"defaultValue": "[subscription().subscriptionId]"
},
"sourceVault": {
"type": "string",
"defaultValue": "[subscription().subscriptionId]"
},
"sourceResourceGroup": {
"type": "string",
"defaultValue": "[resourceGroup().name]"
},
"sourceSecretName": {
"type": "string"
},
"update": {
"type": "bool",
"defaultValue": false
}
},
"variables": {
"empty": {
"value": ""
},
"test": {
"reference": {
"keyVault": {
"id": "[resourceId(subscription().subscriptionId, resourceGroup().name, 'Microsoft.KeyVault/vaults', parameters('keyVaultName'))]"
},
"secretName": "[parameters('secretName')]"
}
}
},
"resources": [
{
"apiVersion": "2018-05-01",
"name": "AddLinkedSecret",
"type": "Microsoft.Resources/deployments",
"properties": {
"mode": "Incremental",
"templateLink": {
"uri": "[concat('https://management.dotnetdevops.org/providers/DotNetDevOps.AzureTemplates/templates/KeyVaults/',parameters('keyVaultName'),'/secrets/',parameters('secretName'))]",
"contentVersion": "1.0.0.0"
},
"parameters": {
"existingValue": "[if(parameters('update'),variables('test'),variables('empty'))]",
"secretValue": {
"reference": {
"keyVault": {
"id": "[resourceId(parameters('sourceVaultSubscription'), parameters('sourceResourceGroup'), 'Microsoft.KeyVault/vaults', parameters('sourceVault'))]"
},
"secretName": "[parameters('sourceSecretName')]"
}
}
}
}
}
],
"outputs": {
"secretUriWithVersion": {
"type": "string",
"value": "[reference('AddLinkedSecret').outputs.secretUriWithVersion.value]"
}
}
}
and that template has a nested call to https://management.dotnetdevops.org/providers/DotNetDevOps.AzureTemplates/templates/KeyVaults/{keyvaultName}/secrets/{secretName} which gives the one with the condition
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"keyVaultName": {
"type": "string",
"defaultValue": "io-board-data-ingest-dev",
"metadata": {
"description": "Name of the existing vault"
}
},
"secretName": {
"type": "string",
"metadata": {
"description": "Name of the secret to store in the vault"
},
"defaultValue": "DataStorage"
},
"secretValue": {
"type": "securestring",
"metadata": {
"description": "Value of the secret to store in the vault"
}
},
"existingValue": {
"type": "securestring",
"defaultValue": ""
}
},
"variables": {},
"resources": [
{
"type": "Microsoft.KeyVault/vaults/secrets",
"condition": "[not(equals(parameters('existingValue'),parameters('secretValue')))]",
"apiVersion": "2015-06-01",
"name": "[concat(parameters('keyVaultName'), '/', parameters('secretName'))]",
"properties": {
"value": "[parameters('secretValue')]"
}
}
],
"outputs": {
"secretUriWithVersion": {
"type": "string",
"value": "[reference(resourceId(resourceGroup().name, 'Microsoft.KeyVault/vaults/secrets', parameters('keyVaultName'), parameters('secretName')), '2015-06-01').secretUriWithVersion]"
}
}
}

Mark a method anonymous: swagger version 3.0.2

I am using Swagger-ui version 3.0.2, I have hosted it locally and provided it my Json file and API it opens the document fine and lists all the method in the json file, after i put basic authentication in it, i did changes in the .JSON file, but there are some methods which i want to mark anonymous.
{
"swagger": "2.0",
"info": {
"description": "description",
"version": "1.0",
"title": "API"
},
"host": "localhost",
"schemes": [
"http"
],
"securityDefinitions": {
"anonymous_auth": {
"type": ""
},
"basic_auth": {
"type": "basic",
"name": "basic_auth",
"description": "Basic Authentication"
},
"token": {
"type": "apiKey",
"description": "API Token Authentication",
"name": "apikey",
"in": "header"
}
},
"security": [
{
"basic_auth": [ ]
},
{
"token": [ ]
}
],
"paths": {
//somthing
},
"definitions": {
//something
}
}
By using security atribute in this way it will secure complete file, but i have some methods which should be anonymous.
To remove global security, add an empty security array to the operation:
"paths": {
"/something:": {
"get": {
"security": [],
...
}
}
}
Also, your spec is not valid:
Remove anonymous_auth.
Remove name from basic_auth - name is only used in apiKey security schemes to specify the name of the header or query parameter that will contain the API key.

How do I use the swagger models section?

Inside the Swagger API Documentation there is inside the json beside the apis array a model object entry but no documentation about it. How can I use this "models" part?
{
apiVersion: "0.2",
swaggerVersion: "1.1",
basePath: "http://petstore.swagger.wordnik.com/api",
resourcePath: "/pet.{format}"
...
apis: [...]
models: {...}
}
Models are nothing but like your POJO classes in java which have variables and properties. In models section you can define your own custom class and you can refer it as data type.
If you see below
{
"path": "/pet.{format}",
"description": "Operations about pets",
"operations": [
{
"httpMethod": "POST",
"summary": "Add a new pet to the store",
"responseClass": "void",
"nickname": "addPet",
"parameters": [
{
"description": "Pet object that needs to be added to the store",
"paramType": "body",
"required": true,
"allowMultiple": false,
"dataType": "Pet"
}
],
"errorResponses": [
{
"code": 405,
"reason": "Invalid input"
}
]
}
Here in parameter section it have one parameter who's dataType is Pet and pet is defined in models as below
{
"models": {
"Pet": {
"id": "Pet",
"properties": {
"id": {
"type": "long"
},
"status": {
"allowableValues": {
"valueType": "LIST",
"values": [
"available",
"pending",
"sold"
]
},
"description": "pet status in the store",
"type": "string"
},
"name": {
"type": "string"
},
"photoUrls": {
"items": {
"type": "string"
},
"type": "Array"
}
}
}
}}
You can have nested models , for more information see Swagger PetStore example
So models are nothing but like classes.

Resources