pulumi-azure-native icon indicating copy to clipboard operation
pulumi-azure-native copied to clipboard

AzureNative.App.ContainerApp failure to add referenced Azure Key Vault secret.

Open mentallabyrinth opened this issue 1 year ago • 9 comments

What happened?

We're trying to add a secret which references Azure Key Vault into our Azure Container App. However, attempts to update fail with this error message:

azure-native:app:ContainerApp (dev-spa-eus2-aca0001):
    error: Code="ContainerAppSecretInvalid" Message="Invalid Request: Container app secret(s) with name(s) 'testing123' are invalid: value or keyVaultUrl and identity should be provided."

Example

Here's the code that adds the secret into the container app.

new AzureNative.App.Inputs.SecretArgs
{
      Name = "testing123",
      KeyVaultUrl = "https://dev-api-kv01.vault.azure.net/secrets/AZUREADCLIENT--CLIENTSECRET",
      Identity = "/subscriptions/<redacted>/resourcegroups/dev-app-eus2-rg0001fdfde73a/providers/Microsoft.ManagedIdentity/userAssignedIdentities/dev-api-eus2-mi0001a15f69b1s"
}

The failure occurs after running pulumi -s dev up

We were successful in adding a key vault referenced secret manually using the same configuration as above in the Azure portal. Inspecting the resource JSON we see the following:

{
      "name": "testing",
      "keyVaultUrl": "https://dev-api-kv01.vault.azure.net/secrets/AZUREADCLIENT--CLIENTSECRET",
      "identity": "/subscriptions/<redacted>/resourcegroups/dev-app-eus2-rg0001fdfde73a/providers/Microsoft.ManagedIdentity/userAssignedIdentities/dev-api-eus2-mi0001a15f69b1"
}

Output of pulumi about

CLI
Version 3.90.1 Go Version go1.21.9 Go Compiler gc

Plugins NAME VERSION azure-native 2.38.0 azuread 5.48.0 azuredevops 3.0.0 dotnet unknown random 4.16.0 twingate 0.0.48

Host
OS darwin Version 13.5.2 Arch arm64

This project is written in dotnet: executable='/nix/store/d38z3dpvqpaq8az30nnlp2lq37fjprbi-dotnet-sdk-8.0.101/bin/dotnet' version='8.0.101'

Dependencies: NAME VERSION Azure.Containers.ContainerRegistry 1.1.1 Pulumi 3.63.0 Pulumi.AzureAD 5.48.0 Pulumi.AzureDevOps 3.0.0 Pulumi.AzureNative 2.38.0 Pulumi.Random 4.16.0

Pulumi locates its logs in /var/folders/mz/vmtwh2n56nzbm7nshzkhfngh0000gn/T/ by default

Additional context

No response

Contributing

Vote on this issue by adding a 👍 reaction. To contribute a fix for this issue, leave a comment (and link to your pull request, if you've opened one already).

mentallabyrinth avatar Apr 26 '24 12:04 mentallabyrinth

This looks specific to Azure Native, so moving to the appropriate repo and someone will take a look soon.

justinvp avatar Apr 26 '24 23:04 justinvp

I've written a complete program to reproduce this issue, see below.

This upstream issue suggests that the error value or keyVaultUrl and identity should be provided can be misleading and the actual problem might be an input validation error. In that light, I've experimented with several modifications of secret naming, identity, etc.; but to no avail.

Via verbose logging I verified that the request Pulumi sends to Azure matches the correct one from the portal, yet the request is denied.

It seems like either an Azure issue to me at this point, or a misconfiguration in the identity and Key Vault access. Pulumi seems to be sending the program input to Azure faithfully.

We could try to reproduce the issue with an ARM template or a similar deployment without Pulumi.

using Pulumi;
using AzureNative = Pulumi.AzureNative;

const string sub = "redacted";
const string tenant = "redacted";
var identity = "redacted";
var identityFull =  Output.Format($"/subscriptions/{sub}/resourceGroups/pulumi-dev-shared/providers/Microsoft.ManagedIdentity/userAssignedIdentities/test-identity");

// Repro for https://github.com/pulumi/pulumi-azure-native/issues/3243
return await Deployment.RunAsync(() =>
{
    var resourceGroup = new AzureNative.Resources.ResourceGroup("rg", new()
    {
        Location = "East US",
    });

    var kv = new AzureNative.KeyVault.Vault("kv", new()
    {
        Location = "East US",
        ResourceGroupName = resourceGroup.Name,
        Properties = new AzureNative.KeyVault.Inputs.VaultPropertiesArgs
        {
            TenantId = tenant,
            Sku = new AzureNative.KeyVault.Inputs.SkuArgs
            {
                Family = "A",
                Name = AzureNative.KeyVault.SkuName.Standard,
            },
            AccessPolicies = new[]
            {
                new AzureNative.KeyVault.Inputs.AccessPolicyEntryArgs
                {
                    TenantId = tenant,
                    ObjectId = identity,
                    Permissions = new AzureNative.KeyVault.Inputs.PermissionsArgs
                    {
                        Secrets = {Union<string, AzureNative.KeyVault.SecretPermissions>.FromT1(AzureNative.KeyVault.SecretPermissions.All)}
                    },
                },            
            },
        },
    });

    var secret = new AzureNative.KeyVault.Secret("secret", new()
    {
        SecretName = "testing",
        VaultName = kv.Name,
        ResourceGroupName = resourceGroup.Name,
        Properties = new AzureNative.KeyVault.Inputs.SecretPropertiesArgs
        {
            Value = "supersecret",
        },
    });

    var managedEnvironment = new AzureNative.App.ManagedEnvironment("managedEnvironment", new()
    {
        EnvironmentName = "testcontainerenv",
        Location = "East US",
        ResourceGroupName = resourceGroup.Name,
        Sku = new AzureNative.App.Inputs.EnvironmentSkuPropertiesArgs
        {
            Name = AzureNative.App.SkuName.Consumption,
        },
    });

    var containerApp = new AzureNative.App.ContainerApp("containerApp", new()
    {
        Configuration = new AzureNative.App.Inputs.ConfigurationArgs
        {
            Secrets = new[]
            {
                new AzureNative.App.Inputs.SecretArgs
                {
                    Name = "testing",
                    KeyVaultUrl = Output.Format($"https://{kv.Name}.azure.net/secrets/{secret.Name}"),
                    Identity = identityFull
                }
            },
        },
        ContainerAppName = "testcontainerapp0",
        EnvironmentId = managedEnvironment.Id,
        Identity = new AzureNative.App.Inputs.ManagedServiceIdentityArgs
        {
            Type = AzureNative.App.ManagedServiceIdentityType.UserAssigned,
            UserAssignedIdentities = { { identityFull } }
        },
        Location = "East US",
        ResourceGroupName = resourceGroup.Name,
        Template = new AzureNative.App.Inputs.TemplateArgs
        {
            Containers = new[]
            {
                new AzureNative.App.Inputs.ContainerArgs
                {
                    Image = "mcr.microsoft.com/deployment-environments/runners/core:latest",
                    Name = "testcontainerapp0",
                    Probes = new[]
                    {
                        new AzureNative.App.Inputs.ContainerAppProbeArgs
                        {
                            HttpGet = new AzureNative.App.Inputs.ContainerAppProbeHttpGetArgs
                            {
                                HttpHeaders = new[]
                                {
                                    new AzureNative.App.Inputs.ContainerAppProbeHttpHeadersArgs
                                    {
                                        Name = "Custom-Header",
                                        Value = "Awesome",
                                    },
                                },
                                Path = "/health",
                                Port = 8080,
                            },
                            InitialDelaySeconds = 3,
                            PeriodSeconds = 3,
                            Type = AzureNative.App.Type.Liveness,
                        },
                    },
                },
            },
            InitContainers = new[]
            {
                new AzureNative.App.Inputs.InitContainerArgs
                {
                    Args = new[]
                    {
                        "-c",
                        "while true; do echo hello; sleep 10;done",
                    },
                    Command = new[]
                    {
                        "/bin/sh",
                    },
                    Image = "mcr.microsoft.com/deployment-environments/runners/core:latest",
                    Name = "testinitcontainerapp0",
                    Resources = new AzureNative.App.Inputs.ContainerResourcesArgs
                    {
                        Cpu = 0.5,
                        Memory = "1Gi",
                    },
                },
            },
            Scale = new AzureNative.App.Inputs.ScaleArgs
            {
                MaxReplicas = 5,
                MinReplicas = 1,
                Rules = new[]
                {
                    new AzureNative.App.Inputs.ScaleRuleArgs
                    {
                        Custom = new AzureNative.App.Inputs.CustomScaleRuleArgs
                        {
                            Metadata =
                            {
                                { "concurrentRequests", "50" },
                            },
                            Type = "http",
                        },
                        Name = "httpscalingrule",
                    },
                },
            },
        },
        WorkloadProfileType = "GeneralPurpose",
    }, new CustomResourceOptions { DependsOn = { secret } } );
});

thomas11 avatar Apr 30 '24 08:04 thomas11

In case you haven't resolved, it was a bug in Azure API. Using container apps api version v20230501 or v20240301 (these two aren't preview) has no issue with KV secrets

MatteoCalabro-TomTom avatar May 03 '24 12:05 MatteoCalabro-TomTom

~~The added docs are live now. I hope that helps.~~

thomas11 avatar May 08 '24 05:05 thomas11

The added docs are live now. I hope that helps.

That is sql server ☺️

MatteoCalabro-TomTom avatar May 08 '24 06:05 MatteoCalabro-TomTom

Gah, I commented on the wrong issue. Sorry!

thomas11 avatar May 08 '24 08:05 thomas11

@thomas11 Have you tried https://github.com/pulumi/pulumi-azure-native/issues/3243#issuecomment-2092929555? Maybe we can close the issue?

mikhailshilkov avatar May 10 '24 10:05 mikhailshilkov

@mikhailshilkov I'm happy to take @MatteoCalabro-TomTom's word for it :)

However, other users could still run into this. I'd like us to

  1. Add docs to this resource that warn of this caveat
  2. File an issue to consider this when picking the v3 default version

thomas11 avatar May 10 '24 12:05 thomas11

@mikhailshilkov I'm happy to take @MatteoCalabro-TomTom's word for it :)

However, other users could still run into this. I'd like us to

  1. Add docs to this resource that warn of this caveat
  2. File an issue to consider this when picking the v3 default version

There should be a new version coming still, which will probably become stable, bringing Otel support to ACA. At the moment, v20230501 seems to be the one used by Azure Portal.

MatteoCalabro-TomTom avatar May 10 '24 12:05 MatteoCalabro-TomTom