Home > OS >  How to use Pulumi Output<string> as a string in .NET
How to use Pulumi Output<string> as a string in .NET

Time:07-21

I have a basic Pulumi build for keycloak where I set up a realm, create a scope, create a client, and update teh scopes for my client.

class RealmBuild : Stack
{
    public RealmBuild()
    {
        var realm = new Realm("ExampleRealm-realm", new RealmArgs
        {
            RealmName = "ExampleRealm"
        });
        var recipemanagementScope = ScopeFactory.CreateScope(realm.Id, "recipe_management");
        
        var recipeManagementPostmanMachineClient = ClientFactory.CreateClientCredentialsFlowClient(realm.Id,
            "recipe_management.postman.machine", 
            "974d6f71-d41b-4601-9a7a-a33084484682", 
            "RecipeManagement Postman Machine",
            "https://oauth.pstmn.io");
        recipeManagementPostmanMachineClient.ExtendDefaultScopes(recipemanagementScope.Name);
    }
}

public static class ClientExtensions
{
    public static void ExtendDefaultScopes(this Client client, params Output<string>[] scopeNames)
    {
        var defaultScopeName = $"default-scopes-for-{client.Name.Apply(x => x)}";
        var defaultScopes = new ClientDefaultScopes(defaultScopeName, new ClientDefaultScopesArgs()
        {
            RealmId = client.RealmId,
            ClientId = client.Id,
            DefaultScopes =
            {
                "openid",
                "profile",
                "email",
                "roles",
                "web-origins",
                scopeNames,
            },
        });
    }
}


public class ClientFactory
{    
    public static Client CreateClientCredentialsFlowClient(Output<string> realmId, 
        string clientId, 
        string clientSecret, 
        string clientName, 
        string baseUrl)
    {
        return new Client($"{clientName.ToLower()}-client", new ClientArgs()
        {
            RealmId = realmId,
            ClientId = clientId,
            Name = clientName,
            StandardFlowEnabled = false,
            Enabled = true,
            ServiceAccountsEnabled = true,
            AccessType = "CONFIDENTIAL",
            BaseUrl = baseUrl,
            AdminUrl = baseUrl,
            ClientSecret = clientSecret,
            BackchannelLogoutSessionRequired = true,
            BackchannelLogoutUrl = baseUrl
        });
    }
}

The problem is, I am getting this error around my scopes:

Diagnostics:
  keycloak:openid:ClientDefaultScopes (default-scopes-for-Calling [ToString] on an [Output<T>] is not supported.

To get the value of an Output<T> as an Output<string> consider:
1. o.Apply(v => $"prefix{v}suffix")
2. Output.Format($"prefix{hostname}suffix");

See https://pulumi.io/help/outputs for more details.
This function may throw in a future version of Pulumi.):
    error: Duplicate resource URN 'urn:pulumi:dev::KeycloakPulumiStack::keycloak:openid/clientDefaultScopes:ClientDefaultScopes::default-scopes-for-Calling [ToString] on an [Output<T>] is not supported.
    
    To get the value of an Output<T> as an Output<string> consider:
    1. o.Apply(v => $"prefix{v}suffix")
    2. Output.Format($"prefix{hostname}suffix");
    
    See https://pulumi.io/help/outputs for more details.
    This function may throw in a future version of Pulumi.'; try giving it a unique name

I tried something like this as well var defaultScopeName = Output.Format($"default-scopes-for-{client.Name}");, but I can't pass that into the name for ClientDefaultScopes

I did look at the docs to see if anything stuck out as an issue, but I'm clearly missing something.

CodePudding user response:

Rule number 1 with Pulumi outputs: Anything you return from an apply() will still be an Output, even if it looks like it should be a string.

In other words, on this line of code:

var defaultScopeName = $"default-scopes-for-{client.Name.Apply(x => x)}";

defaultScopeName is Output<string>.

However, the x variable in the lambda is in fact a string rather than an output.

The other item to note is that the name of a resource (so the first argument) cannot be an Output. So in your code:

var defaultScopeName = $"default-scopes-for-{client.Name.Apply(x => x)}";
var defaultScopes = new ClientDefaultScopes(defaultScopeName, new ClientDefaultScopesArgs()
{
    RealmId = client.RealmId,
    ClientId = client.Id,
    DefaultScopes =
    {
        "openid",
        "profile",
        "email",
        "roles",
        "web-origins",
        scopeNames,
    },
});

because defaultScopeName is an Output, this won't work.

You could create the resource inside of the apply():

var defaultScopea = $"default-scopes-for-{client.Name.Apply(x => 
    return new ClientDefaultScopes(x, new ClientDefaultScopesArgs()
        {
            RealmId = client.RealmId,
            ClientId = client.Id,
            DefaultScopes =
            {
                "openid",
                "profile",
                "email",
                "roles",
                "web-origins",
                scopeNames,
            },
        });
)}";
        

however, this may mean that the resource won't appear in any previews (see the note in the Apply section of the Inputs and Outputs page in the Pulumi docs).

So what's the answer here? it looks like you're setting the ClientName to be a string value earlier in the code, so I'd use the same variable that you're setting there.

CodePudding user response:

You can't mix and match string and Output<string> values. Instead, you need to transform any output and append your static list to the list of resolved values:

var defaultScopeName = Output.Format($"default-scopes-for-{client.Name}");
var defaultScopes = new ClientDefaultScopes("some-scope-name", new ClientDefaultScopesArgs()
{
    RealmId = client.RealmId,
    ClientId = client.Id,
    DefaultScopes = Output.All(scopeNames).Apply(names =>
        new[] { "openid", "profile", "email", "roles", "web-origins", }
        .Concat(names)),
});

Note that Output.Format is used for string formatting, Output.All is used to convert to Output<string[]> and .Apply is used to transform the array. You can learn more in Inputs and Outputs.

CodePudding user response:

Currently, Pulumi only supports string types for the name of a resource. Since var defaultScopeName = $"default-scopes-for-{client.Name.Apply(x => x)}"; is using an output of a resource, defaultScopeName is type Output<string> and can't be used for the resource name in the line, var defaultScopes = new ClientDefaultScopes(defaultScopeName, new ClientDefaultScopesArgs()

If I'm reading the code correctly, you specify clientName and use it to set client.Name. So, I would just pass in clientName and use that instead of client.Name. And, that should work since it's a basic type all the way through.

  • Related