Azure

How To Deploy Azure Operate And Azure Key Vault With Bicep

Introduction

Whereas growing pattern code for my OpenAI GPT shopper, I wished to face up an Azure Operate that solutions direct messages despatched to my Twitter account with responses GPT-3. That is impressed by a pal who has a parody account on Twitter and infrequently will get odd DM messages from strangers. The Azure Operate internet hook responds in accordance with the persona of the parody account. Each Twitter credentials and an OpenAI API key are required. Whereas this was developed primarily as a code pattern for my GPT-Three API shopper Nuget bundle, I nonetheless utilized safe coding practices and saved the credentials in an Azure Key Vault. Additional, I would like the deployment to be as seamless as doable and so I began with an ARM template, however transformed over to a Bicep template. Bicep is less complicated to learn than ARM and extensions are accessible each for VS Code and Visual Studio.

This text exhibits how one can do the next utilizing Bicep:

  1. Deploy an Azure Key Vault
  2. Deploy an Azure Operate Software
  3. Grant the Azure Operate Software entry to secrets and techniques within the Azure Key Vault

Accomplished Bicep and PowerShell scripts can be found right here.

How you can Set up Bicep

First issues first, let’s get Bicep.

Basic pointers for putting in Bicep can be found at Set up Bicep Instruments. The examples on this article use PowerShell.

To make use of Bicep with PowerShell:

  1. Set up (or improve) to PowerShell 5.6.0+ at Set up PowerShell on Home windows
  2. Set up Azure Az Powershell. Open a PowerShell Command immediate as an area admin and run:
    Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
    Set up-Module -Title Az -Scope CurrentUser -Repository PSGallery -Power
    
  3. Obtain and run the most recent Home windows Bicep installer.
  4. Shut any PowerShell terminals and reopen to validate the set up by working:
    bicep --version
    

Convert from ARM to Bicep

Beginning with ARM templates, I discovered the syntax troublesome to comply with when features had been utilized. Take the next instance that concatenates a collection of Twitter credentials right into a JSON string in preparation for storage as an Azure Key Vault secret. The logic is troublesome to learn between the escaped double quotes utilized by JSON and the one quotes of the concat perform.

{
    "kind": "secrets and techniques",
    "title": "twittercreds",
    "apiVersion": "2016-10-01",
    "properties": {
        "worth": "[concat('{ "AccessToken": "', parameters('twitterAccessToken'), '", "AccessTokenSecret": "', parameters('twitterAccessTokenSecret'), '", "ConsumerKey": "', parameters('twitterConsumerKey'),'", "ConsumerSecret": "', parameters('twitterConsumerSecret'), '"}')]"
    }
}

Changing from ARM to Bicep is an easy command line operation.

az bicep decompile --file .twitterfunc.jsonc

The identical logic is expressed within the Bicep template utilizing string interpolation.

useful resource twitterchatgpt_dev_kv01_twittercreds 'Microsoft.KeyVault/vaults/secrets and techniques@2016-10-01' = {
    dad or mum: twitterchatgpt_dev_kv01
    title: 'twittercreds'
    properties: {
        worth: '{ "AccessToken": "${twitterAccessToken}", "AccessTokenSecret": "${twitterAccessTokenSecret}", "ConsumerKey": "${twitterConsumerKey}", "ConsumerSecret": "${twitterConsumerSecret}"}'
    }
}

That is a lot simpler to learn than the unique implementation; nevertheless, I did run right into a conversion challenge with dependsOn references. This block from the unique ARM template resulted in an error.

{
  "title": "functionName",
  "kind": "Microsoft.Internet/websites",
  . . . 
  "dependsOn": [
    "[resourceId('Microsoft.Web/serverfarms', 'hostingPlanName')]",
    "[resourceId('Microsoft.Storage/storageAccounts', 'storageAccountName')]"
  ],

Bicep translated these as errors that surfaced in Visual Studio Code:

ARM is extra forgiving in accepting plain textual content to reference useful resource. I referred to as out these dependencies for the sake of express documentation. They don’t seem to be strictly needed and so I eliminated them.

Transpiling to ARM – There and Again Once more

Like working a translation of 1 language to a different in Google Translate and again once more, it is useful to see how the translated phrase comes again to the unique language. When deploying a Bicep template, Azure transpires to ARM-JSON. We are able to do the identical factor on the command line with:

bicep construct twitterfunc.bicep

Within the string concatenation instance, Bicep opted for a format perform quite than concat. Whereas simpler to learn than the unique, Bicep’s use of string interpolation is the clearest implementation.

{
    "kind": "Microsoft.KeyVault/vaults/secrets and techniques",
    "apiVersion": "2016-10-01",
    "title": "[format('{0}/{1}', 'twitterchatgpt-dev', 'twittercreds')]",
    "properties": {
        "worth": "[format('{{ "AccessToken": "{0}", "AccessTokenSecret": "{1}", "ConsumerKey": "{2}", "ConsumerSecret": "{3}"}}', parameters('twitterAccessToken'), parameters('twitterAccessTokenSecret'), parameters('twitterConsumerKey'), parameters('twitterConsumerSecret'))]"
    },
    "dependsOn": ["[resourceId('Microsoft.KeyVault/vaults', 'twitterchatgpt-dev')]"]
}

The ARM template translated again from Bicep added extra dependencies to the Azure perform than I had initially, however this time it used variables quite than express useful resource names.

"dependsOn": [
  "[resourceId('Microsoft.Insights/components', variables('applicationInsightsName'))]",
  "[resourceId('Microsoft.Web/serverfarms', variables('hostingPlanName'))]",
  "[resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName'))]",
  "[resourceId('Microsoft.KeyVault/vaults', variables('keyVaultName'))]",
  "[resourceId('Microsoft.KeyVault/vaults/secrets', variables('keyVaultName'), 'openaicreds')]",
  "[resourceId('Microsoft.KeyVault/vaults/secrets', variables('keyVaultName'), 'twittercreds')]"
]

Constructing the Bicep Template

This template relies on the Bicep file at Quickstart: Create and deploy Azure Capabilities assets utilizing Bicep which features a storage account, internet hosting plan, perform, and Software Insights. The following steps assume familiarity with that template. The template is saved as twitterfunc.bicep. It doesn’t embody safe parameters or an AKV (Azure Key Vault). This part will add the AKV, secrets and techniques, and grant rights to the Azure Operate Software to the secrets and techniques utilizing least-privilege rules.

First, we’ll add parameters to simply accept safe strings.

@description('Twitter credentials in JSON.')
@safe()
param twitterCredsJson string

@description('OpenAI credentials in JSON.')
@safe()
param openAIAPICredsJson string

It is a departure from the prior configuration settings the place the Twitter credentials had been constructed from concatenated strings. Right here, the credentials are already handed as JSON strings. Furthermore, they’re handed as safe strings which prevents their plain textual content values from showing in any deployment logs.

Subsequent comes the AKV. 

useful resource twitterchatgpt_dev_kv 'Microsoft.KeyVault/vaults@2019-09-01' = {
    title: keyVaultName
    location: twitgptgrouppname
    tags: {
        displayName: 'twitterchatgpt'
    }
    properties: {
        enabledForDeployment: true
        enabledForTemplateDeployment: true
        enabledForDiskEncryption: true
        tenantId: tenantId
        enableSoftDelete: true
        accessPolicies: []
        sku: {
            title: 'normal'
            household: 'A'
        }
    }
}

Be aware that it doesn’t apply any entry insurance policies. We’ll grant Get entry to the secrets and techniques to the Azure perform later. Subsequent come the secrets and techniques.

useful resource twitterchatgpt_dev_kv_twittercreds 'Microsoft.KeyVault/vaults/secrets and techniques@2016-10-01' = {
    dad or mum: twitterchatgpt_dev_kv
    title: 'twittercreds'
    tags: {
        displayName: 'twitterchatgpt'
    }
    properties: {
        worth: twitterCredsJson
    }
}
useful resource twitterchatgpt_dev_kv_openaiapicreds 'Microsoft.KeyVault/vaults/secrets and techniques@2016-10-01' = {
    dad or mum: twitterchatgpt_dev_kv
    title: 'openaicreds'
    tags: {
        displayName: 'twitterchatgpt'
    }
    properties: {
        worth: openAIAPICredsJson
    }
}

The Twitter and OpenAI credential safe strings are saved to our AKV secrets and techniques.

useful resource
perform 'Microsoft.Internet/websites@2020-12-01' = {
    title: appName
    location: twitgptgrouppname
    type: 'functionapp'
    tags: {
        displayName: 'twitterchatgpt'
    }
    id: {
        kind: 'SystemAssigned'
    }
    properties: {
        serverFarmId: hostingPlan.id
        siteConfig: {
            ftpsState: 'Disabled'
            minTlsVersion: '1.2'
            http20Enabled: true
            appSettings: overwriteFuncSettings ? [... {
                name: 'TWITTER_CREDS'
                value: '@Microsoft.KeyVault(VaultName=${twitterchatgpt_dev_kv.name};SecretName=${twitterchatgpt_dev_kv_twittercreds.name})'
            } {
                name: 'OPENAI_API_CREDS'
                value: '@Microsoft.KeyVault(VaultName=${twitterchatgpt_dev_kv.name};SecretName=${twitterchatgpt_dev_kv_openaiapicreds.name})'
            }] : null
        }
        httpsOnly: true
    }
}

The @Microsoft.KeyVault macro perform reads the Twitter and OpenAI secrets and techniques from the AKV and passes the decrypted values to the Azure Operate. No additional code is required within the Azure Operate to learn the credentials. They’re referenced from setting variables like another perform app setting.

IConfiguration config = context.Configuration;
string? twitterCredString = config["TWITTER_CREDS"];

WebhookCredentials? twitterCreds = string.IsNullOrWhiteSpace(twitterCredString) ?
    null : JsonSerializer.Deserialize<WebhookCredentials>(twitterCredString);

At this level, the Azure Operate has not been granted rights to the AKV secrets and techniques. We’ve got given the Azure Operate a SystemAssigned managed id.

id: {
    kind: 'SystemAssigned'
}

That is surfaced within the Azure portal.

How To Deploy Azure Function and Azure Key Vault with Bicep

And the configuration settings level to the Azure Key Vault.

How To Deploy Azure Function and Azure Key Vault with Bicep

The Bicep visualization of the template offers us:

How To Deploy Azure Function and Azure Key Vault with Bicep

Now we now have a problem with the order of operations. The Azure Operate has a reference to the AKV secrets and techniques however does not but have entry to them. The AKV was already created and can’t be modified from the identical template. In an ARM template, this might be resolved utilizing a subtemplate. With Bicep, this may be utilized in a module that updates the AKV insurance policies to grant Get secret rights to the Azure Operate’s system id. On the finish of the template, add:

module appService 'updateakv.bicep' = {
    title: 'appService'
    params: {
        funcName: perform.title
        location: location
        keyVaultName: keyVaultName
    }
}

This module passes the perform title, useful resource group, and AKV title. After which create a brand new Bicep file in the identical listing named updateakv.bicep with the contents:

@description('Location for all assets.')
param funcName string
@description('Location for all assets.')
param location string = resourceGroup().location
@minLength(3)
@maxLength(24)
@description('Title of the keyvault that shops Twitter and OpenAI credentials.')
param keyVaultName string
var tenantId = tenant().tenantId
useful resource
perform 'Microsoft.Internet/websites@2020-12-01'
current = {
    title: funcName
}
useful resource twitterchatgpt_dev_kv 'Microsoft.KeyVault/vaults@2019-09-01' = {
    title: keyVaultName
    location: location
    tags: {
        displayName: 'twitterchatgpt'
    }
    properties: {
        tenantId: tenantId
        accessPolicies: [{
            tenantId: tenantId
            objectId: function.identity.principalId
            permissions: {
                secrets: ['get']
            }
        }]
        sku: {
            title: 'normal'
            household: 'A'
        }
    }
}

This might have simply handed the principalId of the keyvault, however it’s useful to point out that Bicep modules can reference current assets. Right here, the Azure Operate Software is used as an current useful resource to tug the principalId from the perform app’s configured id and grant that id Get rights to the AKV secrets and techniques. This ends in a a lot less complicated template visualization.

How To Deploy Azure Function and Azure Key Vault with Bicep

This template ends in the next entry coverage grant.

How To Deploy Azure Function and Azure Key Vault with Bicep

Lastly, the deployment is kicked off with a PowerShell script which creates the twitter-chatgpt useful resource group within the East US area. Whereas we might use a parameter file, that will require typing the secrets and techniques in for each deployment because the parameter file cannot retailer encrypted secrets and techniques. As a substitute, the OpenAI and Twitter credentials are learn in from setting variables, saved as SecureStrings, and handed as safe parameters.

$resourceGroupName = "twitter-chatgpt"
New - AzResourceGroup `
  -Title $resourceGroupName ` - Location "East US"
$templateFile = ".twitterfunc.bicep"
$twitterCredsJson = ConvertTo - SecureString $Env: TWITTER_CREDS - AsPlainText - Power
$OpenAIAPICreds = ConvertTo - SecureString $Env: OPENAI_API_CREDS - AsPlainText - Power
New - AzResourceGroupDeployment `
  -Title twitter-chatgpt-resources ` - ResourceGroupName $resourceGroupName `
  -TemplateFile $templateFile ` - twitterCredsJson $twitterCredsJson `
  -openAIAPICredsJson $OpenAIAPICreds

Azure Operate Redeployments

I did expertise sudden habits when redeploying the Bicep template. Each time the Bicep template was redeployed, I needed to redeploy my Azure Operate from Visual Studio as properly. There are assets on-line that counsel setting WEBSITE_RUN_FROM_PACKAGE to 1 when deploying the Azure Operate as a bundle from Visual Studio.

{
    title: 'WEBSITE_RUN_FROM_PACKAGE'
    worth: '1'
}

This didn’t work in my expertise. I additionally tried working the New-AzureResourceGroupDeployment command with IncrementalMode however that did not work both.

I discovered that redeploying the Bicep template with out making use of software settings left my prior Azure Operate deployment intact.

New-AzResourceGroupDeployment `
  -Title twitter-chatgpt-resources `
  -ResourceGroupName $resourceGroupName `
  -TemplateFile $templateFile `
  -twitterCredsJson $twitterCredsJson `
  -openAIAPICredsJson $OpenAIAPICreds `
  -overwriteFuncSettings $true

The setting is utilized in a ternary operation on appSettings. If true, then appSettings are offered. False, no appSettings are despatched.

appSettings: overwriteFuncSettings ? [... {
    name: 'TWITTER_CREDS'
    value: '@Microsoft.KeyVault(VaultName=${twitterchatgpt_dev_kv.name};SecretName=${twitterchatgpt_dev_kv_twittercreds.name})'
} {
    name: 'OPENAI_API_CREDS'
    value: '@Microsoft.KeyVault(VaultName=${twitterchatgpt_dev_kv.name};SecretName=${twitterchatgpt_dev_kv_openaiapicreds.name})'
}] : null

Abstract

This walkthrough is knowledgeable by my experiences configuring and deploying a Twitter webhook that works with the OpenAI GPT-Three API shopper following a least-privilege method. Solely the service principal of the Azure Operate has learn entry to the OpenAI and Twitter credentials. The Bicep templates and the PowerShell script can be found right here. If you want to see extra articles about this mission, like how one can configure a Twitter webhook, let me know within the feedback.

Tags
Show More

Related Articles

Leave a Reply

Your email address will not be published. Required fields are marked *

Close