从Azure函数访问KeyVault机密失败。

huangapple go评论106阅读模式
英文:

accessing keyvault secrets from azure functions fails

问题

由于某些限制,我无法首先在本地测试 Azure 函数,然后再部署它。
因此,我们采用了使用流水线的方法来解决这个问题。
我在 Azure Git 中有一个存储库,在其中有一个文件夹(azure-functions),其中包含 init.py、functions.json 和 requirement.txt。

我使用以下脚本在流水线 yaml 中创建一个新函数。

func init LocalFunctionProj --python
cd LocalFunctionProj
func new --name TimeTrigger --template "Timer trigger" --schedule "0 */5 * * * *"

现在,上述命令创建了一个新的 init.py、functions.json 和 requirement.txt。
我将这些文件的内容从 azure-functions(在我的存储库中)复制到上面创建的文件中。

现在,我有一个 init.py,在其中我尝试访问虚拟关键词值,但最终遇到了模块未找到的错误。

init.py

import datetime
import logging

import azure.functions as func
import azure.keyvault.secrets as kv_secrets
import azure.identity as az_identity
#from azure.keyvault.secrets import SecretClient
#from azure.identity import DefaultAzureCredential


def main(mytimer: func.TimerRequest) -> None:
    utc_timestamp = datetime.datetime.utcnow().replace(
        tzinfo=datetime.timezone.utc).isoformat()

    if mytimer.past_due:
        logging.info('The timer is past due!')
    
    KVUri = f"XXXXXX"
    credential = az_identity.DefaultAzureCredential()
    logging.info('credential info is %s',credential)
    #client = SecretClient(vault_url=KVUri, credential=credential)

    client = az_identity.SecretClient(vault_url=KVUri, credential=credential)
    password = client.get_secret('TestSecret').value
    logging.info('Secret value is %s', password)


    logging.info('Hello!! Good day!')
    logging.info('Python timer trigger function ran at %s', utc_timestamp)

requirement.txt 包含以下条目:

azure-functions
azure-common==1.1.28
azure-core==1.28.0
azure-identity==1.13.0
azure-keyvault-secrets==4.7.0

我使用流水线部署函数,并且部署成功。
时间触发器的响应显示如下:

Result: Failure Exception: ModuleNotFoundError: No module named 'azure.keyvault'. Please check the requirements.txt file for the missing module. For more info, please refer the troubleshooting guide: https://aka.ms/functions-modulenotfound Stack: File "/azure-functions-host/workers/python/3.9/LINUX/X64/azure_functions_worker/dispatcher.py", line 380, in _handle__function_load_request func = loader.load_function( File "/azure-functions-host/workers/python/3.9/LINUX/X64/azure_functions_worker/utils/wrappers.py", line 48, in call raise extend_exception_message(e, message) File "/azure-functions-host/workers/python/3.9/LINUX/X64/azure_functions_worker/utils/wrappers.py", line 44, in call return func(*args, **kwargs) File "/azure-functions-host/workers/python/3.9/LINUX/X64/azure_functions_worker/loader.py", line 132, in load_function mod = importlib.import_module(fullmodname) File "/usr/local/lib/python3.9/importlib/__init__.py", line 127, in import_module return _bootstrap._gcd_import(name[level:], package, level) File "/home/site/wwwroot/TimeTrigger/__init__.py", line 5, in <module> import azure.keyvault.secrets as kv_secrets
英文:

Due to some limitations, I am not able to test the azure function first locally and then deploy it.
So, we have followed the approach of working around, with the use of pipelines.
I have a repository in Azure git, where I have a folder (azure-functions) containing init.py, functions.json and requirement.txt.

I create a new function from the pipeline using the following script inside the pipeline yaml.

func init LocalFunctionProj --python
cd LocalFunctionProj
func new --name TimeTrigger --template "Timer trigger" --schedule "0 */5 * * * *"

Now the above command creates a new init.py , functions.json and the requirement.txt.
I copy the contents of the files from azure-functions(in my repo) to these files created above.

Now, I have the init.py where I try to access the dummy keyvault value, but end up facing with the module not found error.

init.py

import datetime
import logging

import azure.functions as func
import azure.keyvault.secrets as kv_secrets
import azure.identity as az_identity
#from azure.keyvault.secrets import SecretClient
#from azure.identity import DefaultAzureCredential


def main(mytimer: func.TimerRequest) -> None:
    utc_timestamp = datetime.datetime.utcnow().replace(
        tzinfo=datetime.timezone.utc).isoformat()

    if mytimer.past_due:
        logging.info('The timer is past due!')
    
    KVUri = f"XXXXXX"
    credential = az_identity.DefaultAzureCredential()
    logging.info('credential info is %s',credential)
    #client = SecretClient(vault_url=KVUri, credential=credential)

    client = az_identity.SecretClient(vault_url=KVUri, credential=credential)
    password = client.get_secret('TestSecret').value
    logging.info('Secret value is %s', password)


    logging.info('Hello!! Good day!')
    logging.info('Python timer trigger function ran at %s', utc_timestamp)

The requirement.txt has the following entries :

azure-functions
azure-common==1.1.28
azure-core==1.28.0
azure-identity==1.13.0
azure-keyvault-secrets==4.7.0

I deploy the function using the pipeline itself, and it is successful.
The time triggered response shows the following:

Result: Failure Exception: ModuleNotFoundError: No module named 'azure.keyvault'. Please check the requirements.txt file for the missing module. For more info, please refer the troubleshooting guide: https://aka.ms/functions-modulenotfound Stack: File "/azure-functions-host/workers/python/3.9/LINUX/X64/azure_functions_worker/dispatcher.py", line 380, in _handle__function_load_request func = loader.load_function( File "/azure-functions-host/workers/python/3.9/LINUX/X64/azure_functions_worker/utils/wrappers.py", line 48, in call raise extend_exception_message(e, message) File "/azure-functions-host/workers/python/3.9/LINUX/X64/azure_functions_worker/utils/wrappers.py", line 44, in call return func(*args, **kwargs) File "/azure-functions-host/workers/python/3.9/LINUX/X64/azure_functions_worker/loader.py", line 132, in load_function mod = importlib.import_module(fullmodname) File "/usr/local/lib/python3.9/importlib/__init__.py", line 127, in import_module return _bootstrap._gcd_import(name[level:], package, level) File "/home/site/wwwroot/TimeTrigger/__init__.py", line 5, in <module> import azure.keyvault.secrets as kv_secrets

答案1

得分: 1

I tried running pip install -r requirements.txt and then ran func host start and the Function ran but it gave an error in your init.py code.

My requirements.txt:

azure-functions
azure-common==1.1.28
azure-core==1.28.0
azure-identity==1.13.0
azure-keyvault-secrets==4.7.0
azure-keyvault

As azure identity class does not include SecretClient, It gave an error. I replaced your code -

client = az_identity.SecretClient(vault_url=KVUri, credential=credential)

with the code below and it worked, I also replaced DefaultAzureCredentials with AzureCliCredentials like below:-

import datetime
import logging

import azure.functions as func
import azure.keyvault.secrets as kv_secrets
import azure.identity as az_identity

def main(mytimer: func.TimerRequest) -> None:
    utc_timestamp = datetime.datetime.utcnow().replace(
        tzinfo=datetime.timezone.utc).isoformat()

    if mytimer.past_due:
        logging.info('The timer is past due!')
    
    KVUri = f"https://keyvaultvalley.vault.azure.net/"
    credential = az_identity.AzureCliCredential()

    # Use SecretClient from azure.keyvault.secrets
    client = kv_secrets.SecretClient(vault_url=KVUri, credential=credential)
    password = client.get_secret('TestSecret').value
    logging.info('Secret value is %s', password)

    logging.info('Hello!! Good day!')
    logging.info('Python timer trigger function ran at %s', utc_timestamp)

Local:-

从Azure函数访问KeyVault机密失败。

But AzureCliCredentials won't work in Azure Functions Deployment via Devops as Function app cannot authenticate with your Azure account and key vault via CLI as the interactive authentication is not supported in Function app. One option is to create Azure Service principal and assign it a role to Key vault and deploy the code in function app another way is to authenticate the existing code with ManagedIdentityCredentials with Function app.

init.py with ManagedIdentityCredentials:-

import datetime
import logging

import azure.functions as func
import azure.keyvault.secrets as kv_secrets
import azure.identity as az_identity
az_identity.ManagedIdentityCredential()
def main(mytimer: func.TimerRequest) -> None:
    utc_timestamp = datetime.datetime.utcnow().replace(
        tzinfo=datetime.timezone.utc).isoformat()

    if mytimer.past_due:
        logging.info('The timer is past due!')
    
    KVUri = f"https://keyvaultvalley.vault.azure.net/"
    credential = az_identity.ManagedIdentityCredential()

    # Use SecretClient from azure.keyvault.secrets
    client = kv_secrets.SecretClient(vault_url=KVUri, credential=credential)
    password = client.get_secret('TestSecret').value
    logging.info('Secret value is %s', password)

    logging.info('Hello!! Good day!')
    logging.info('Python timer trigger function ran at %s', utc_timestamp)

Enable System assigned managed identity for your Function app like below:-

从Azure函数访问KeyVault机密失败。

Add Key vault Contributor role to Above Function app managed identity on Key vault and assign access policy to access Secret on Key vault.

从Azure函数访问KeyVault机密失败。

从Azure函数访问KeyVault机密失败。

Now, I uploaded my Function source code file to Azure repository and ran the Pipeline in Devops like below:-

My Azure repository:-

从Azure函数访问KeyVault机密失败。

My function.json:-

{
  "scriptFile": "__init__.py",
  "bindings": [
    {
      "name": "mytimer",
      "type": "timerTrigger",
      "direction": "in",
      "schedule": "0 */1 * * * *"
    }
  ]
}

host.json:-

{
  "version": "2.0",
  "logging": {
    "applicationInsights": {
      "samplingSettings": {
        "isEnabled": true,
        "excludedTypes": "Request"
      }
    }
  },
  "extensionBundle": {
    "id": "Microsoft.Azure.Functions.ExtensionBundle",
    "version": "[3.*, 4.0.0)"
  }
}

requirements.txt:-

azure-functions
azure-common==1.1.28
azure-core==1.28.0
azure-identity==1.13.0
azure-keyvault-secrets==4.7.0
azure-keyvault

*Select new Pipeline your Azure repository with functions code and use existing template like below:-

从Azure函数访问KeyVault机密失败。

Select your subscription correctly and your Function app when asked for prompt after selection the option above.

Yaml pipeline script:-

trigger:
- main

variables:
  
  azureSubscription: 'xxxxxxxx4-c61cd03e7111'

  
  functionAppName: 'siliconfunc210'

  
  vmImageName: 'ubuntu-latest'

  
  workingDirectory: '$(System.DefaultWorkingDirectory)/'

stages:
- stage: Build
  displayName: Build stage

  jobs:
  - job: Build
    displayName: Build
    pool:
      vmImage: $(vmImageName)

    steps:
    - bash: |
        if [ -f extensions.csproj ]
        then
            dotnet build extensions.csproj --runtime ubuntu.16.04-x64 --output ./bin
        fi        
      workingDirectory: $(workingDirectory)
      displayName: 'Build extensions'

    - task: UsePythonVersion@0
      displayName: 'Use Python 3.10'
      inputs:
        versionSpec: 3.10 

    - bash: |
                pip install --target="./.python_packages/lib/site-packages" -r ./requirements.txt
      workingDirectory: $(workingDirectory)
      displayName: &#

<details>
<summary>英文:</summary>

I tried running `pip install -r requirements.txt` and then ran `func host start` and the Function ran but it gave an error in your init.py code.

**My requirements.txt:-**

```txt
azure-functions
azure-common==1.1.28
azure-core==1.28.0
azure-identity==1.13.0
azure-keyvault-secrets==4.7.0
azure-keyvault

As azure identity class does not include SecretClient, It gave an error. I replaced your code -

client = az_identity.SecretClient(vault_url=KVUri, credential=credential)

with the code below and it worked, I also replaced DefaultAzureCredentials with AzureCliCredentials like below:-

import datetime
import logging

import azure.functions as func
import azure.keyvault.secrets as kv_secrets
import azure.identity as az_identity

def main(mytimer: func.TimerRequest) -&gt; None:
    utc_timestamp = datetime.datetime.utcnow().replace(
        tzinfo=datetime.timezone.utc).isoformat()

    if mytimer.past_due:
        logging.info(&#39;The timer is past due!&#39;)
    
    KVUri = f&quot;https://keyvaultvalley.vault.azure.net/&quot;
    credential = az_identity.AzureCliCredential()

    # Use SecretClient from azure.keyvault.secrets
    client = kv_secrets.SecretClient(vault_url=KVUri, credential=credential)
    password = client.get_secret(&#39;TestSecret&#39;).value
    logging.info(&#39;Secret value is %s&#39;, password)

    logging.info(&#39;Hello!! Good day!&#39;)
    logging.info(&#39;Python timer trigger function ran at %s&#39;, utc_timestamp)

Local:-

从Azure函数访问KeyVault机密失败。

But AzureCliCredentials won't work in Azure Functions Deployment via Devops as Function app cannot authenticate with your Azure account and key vault via CLI as the interactive authentication is not supported in Function app. One option is to create Azure Service principal and assign it a role to Key vault and deploy the code in function app another way is to authenticate the existing code with ManagedIdentityCredentials with Function app.

init.py with ManagedIdentityCredentials:-

import datetime
import logging

import azure.functions as func
import azure.keyvault.secrets as kv_secrets
import azure.identity as az_identity
az_identity.ManagedIdentityCredential()
def main(mytimer: func.TimerRequest) -&gt; None:
    utc_timestamp = datetime.datetime.utcnow().replace(
        tzinfo=datetime.timezone.utc).isoformat()

    if mytimer.past_due:
        logging.info(&#39;The timer is past due!&#39;)
    
    KVUri = f&quot;https://keyvaultvalley.vault.azure.net/&quot;
    credential = az_identity.ManagedIdentityCredential()

    # Use SecretClient from azure.keyvault.secrets
    client = kv_secrets.SecretClient(vault_url=KVUri, credential=credential)
    password = client.get_secret(&#39;TestSecret&#39;).value
    logging.info(&#39;Secret value is %s&#39;, password)

    logging.info(&#39;Hello!! Good day!&#39;)
    logging.info(&#39;Python timer trigger function ran at %s&#39;, utc_timestamp)

Enable System assigned managed identity for your Function app like below:-

从Azure函数访问KeyVault机密失败。

Add Key vault Contributor role to Above Function app managed identity on Key vault and assign access policy to access Secret on Key vault.

从Azure函数访问KeyVault机密失败。

从Azure函数访问KeyVault机密失败。

Now, I uploaded my Function source code file to Azure repository and ran the Pipeline in Devops like below:-

My Azure repository:-

从Azure函数访问KeyVault机密失败。

My function.json:-

{
  &quot;scriptFile&quot;: &quot;__init__.py&quot;,
  &quot;bindings&quot;: [
    {
      &quot;name&quot;: &quot;mytimer&quot;,
      &quot;type&quot;: &quot;timerTrigger&quot;,
      &quot;direction&quot;: &quot;in&quot;,
      &quot;schedule&quot;: &quot;0 */1 * * * *&quot;
    }
  ]
}

host.json:-

{
  &quot;version&quot;: &quot;2.0&quot;,
  &quot;logging&quot;: {
    &quot;applicationInsights&quot;: {
      &quot;samplingSettings&quot;: {
        &quot;isEnabled&quot;: true,
        &quot;excludedTypes&quot;: &quot;Request&quot;
      }
    }
  },
  &quot;extensionBundle&quot;: {
    &quot;id&quot;: &quot;Microsoft.Azure.Functions.ExtensionBundle&quot;,
    &quot;version&quot;: &quot;[3.*, 4.0.0)&quot;
  }
}

requirements.txt:-

azure-functions
azure-common==1.1.28
azure-core==1.28.0
azure-identity==1.13.0
azure-keyvault-secrets==4.7.0
azure-keyvault

Select new Pipeline your Azure repository with functions code and use existing template like below:-

从Azure函数访问KeyVault机密失败。

Select your subscription correctly and your Function app when asked for prompt after selection the option above.

Yaml pipeline script:-

trigger:
- main

variables:
  
  azureSubscription: &#39;xxxxxxxx4-c61cd03e7111&#39;

  
  functionAppName: &#39;siliconfunc210&#39;

  
  vmImageName: &#39;ubuntu-latest&#39;

  
  workingDirectory: &#39;$(System.DefaultWorkingDirectory)/&#39;

stages:
- stage: Build
  displayName: Build stage

  jobs:
  - job: Build
    displayName: Build
    pool:
      vmImage: $(vmImageName)

    steps:
    - bash: |
        if [ -f extensions.csproj ]
        then
            dotnet build extensions.csproj --runtime ubuntu.16.04-x64 --output ./bin
        fi        
      workingDirectory: $(workingDirectory)
      displayName: &#39;Build extensions&#39;

    - task: UsePythonVersion@0
      displayName: &#39;Use Python 3.10&#39;
      inputs:
        versionSpec: 3.10 

    - bash: |
                pip install --target=&quot;./.python_packages/lib/site-packages&quot; -r ./requirements.txt
      workingDirectory: $(workingDirectory)
      displayName: &#39;Install application dependencies&#39;

    - task: ArchiveFiles@2
      displayName: &#39;Archive files&#39;
      inputs:
        rootFolderOrFile: &#39;$(workingDirectory)&#39;
        includeRootFolder: false
        archiveType: zip
        archiveFile: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip
        replaceExistingArchive: true

    - publish: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip
      artifact: drop

- stage: Deploy
  displayName: Deploy stage
  dependsOn: Build
  condition: succeeded()

  jobs:
  - deployment: Deploy
    displayName: Deploy
    environment: &#39;development&#39;
    pool:
      vmImage: $(vmImageName)

    strategy:
      runOnce:
        deploy:

          steps:
          - task: AzureFunctionApp@1
            displayName: &#39;Azure functions app deploy&#39;
            inputs:
              azureSubscription: &#39;$(azureSubscription)&#39;
              appType: functionAppLinux
              appName: $(functionAppName)
              package: &#39;$(Pipeline.Workspace)/drop/$(Build.BuildId).zip&#39;

Devops pipeline ran successfully:-

从Azure函数访问KeyVault机密失败。

Output Azure Function app:-

从Azure函数访问KeyVault机密失败。

huangapple
  • 本文由 发表于 2023年8月10日 15:08:44
  • 转载请务必保留本文链接:https://go.coder-hub.com/76873358.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定