-
Notifications
You must be signed in to change notification settings - Fork 8
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
In Azure MS Entra ID custom application registration AzureAD.AppRoleAssignment disappears on the first run of the Azure DevOps deployment pipeline #1785
Comments
Hi @rkolmakov337, it seems like you might be hitting the same problem as pulumi/pulumi-azure-native#1169. Could you try what Daniel wrote there under "Workaround"? |
Hello @thomas11, thank you for the reply. I was not able to locate the workaround. Could you provide a link to it? Thank you. |
Sorry, I linked to the wrong issue. This is the comment containing the workaround.. |
I appreciated the quick reply @thomas11 . Please view below the configuration of the Azure AD custom application for my web API with two AppRoles ("Todo.Read" and "Todo.Write"). The AppRoles do not disappear from the application configuration. The issue is that the Pulumi.AzureAD.AppRoleAssignment disappears for the client who consumes my web API. The consumer gets 401 (Unauthorized). Please view the code below. var application = new Pulumi.AzureAD.Application($"appreg-myapp-dev", new AzureAD.ApplicationArgs
{
DisplayName = $"API Server dev",
IdentifierUris =
{
$"api://app-myapp-dev-server"
},
Api = new ApplicationApiArgs
{
RequestedAccessTokenVersion = 2
},
SinglePageApplication = new ApplicationSinglePageApplicationArgs
{
RedirectUris =
{
Output.Format($"https://{server.DefaultHostName}/swagger/oauth2-redirect.html"),
"https://localhost:7167/swagger/oauth2-redirect.html"
}
},
AppRoles =
{
CreateRole(todoReadRoleUuid.Result, "Todo.Read"),
CreateRole(todoWriteRoleUuid.Result, "Todo.Write")
}
});
private static ApplicationAppRoleArgs CreateRole(Output<string> id, string name)
{
return new ApplicationAppRoleArgs
{
AllowedMemberTypes =
{
"Application"
},
DisplayName = name,
Enabled = true,
Value = name,
Description = name,
Id = id
};
} Pulumi.AzureAD.AppRoleAssignment configuration: private Pulumi.AzureAD.AppRoleAssignment AssignRead(string prefix, string environment, string assigneeName, Output<string> principalObjectId)
{
return new Pulumi.AzureAD.AppRoleAssignment(
$"appra-{prefix}-{assigneeName}-read-server-{environment}",
new AzureAD.AppRoleAssignmentArgs
{
AppRoleId = TodoReadRoleUuid,
PrincipalObjectId = principalObjectId,
ResourceObjectId = ServicePrincipalObjectId,
});
}
private Pulumi.AzureAD.AppRoleAssignment AssignWrite(string prefix, string environmnent, string assigneeName, Output<string> principalObjectId)
{
return new Pulumi.AzureAD.AppRoleAssignment(
$"appra-{prefix}-{assigneeName}-write-server-{environmnent}",
new AzureAD.AppRoleAssignmentArgs
{
AppRoleId = TodoWriteRoleUuid,
PrincipalObjectId = principalObjectId,
ResourceObjectId = ServicePrincipalObjectId
});
}
public void AssignRoles(string prefix, string assigneeName, string environment, Output<string> principalObjectId)
{
AssignRead(prefix, environment, assigneeName, principalObjectId);
AssignWrite(prefix, environment, assigneeName, principalObjectId);
} I call these methods in my stack constructor:
|
Hi @rkolmakov337, thanks for the added context. Unfortunately, I'm still not 100% clear on how everything fits together. What's the definition of Ideally, could you post a complete runnable sample program? Otherwise it might be hard to diagnose your problem. |
Hello @thomas11, Thank you for your reply. I appreciate your help. Please view the code below for web server, pulumi stack, and external client. The issue is that Pulumi.AzureAD.AppRoleAssignment(s) gets deleted on the first run of the Azure DevOps pipeline. The second run of the Azure DevOps pipeline adds the Pulumi.AzureAD.AppRoleAssignment(s) back. I have to manually verify that Role Assignments for each external client have not been deleted. It is fine for one or two external clients but for many it becomes an issue. I thought that there was a problem with the permissions of the service principal of the Azure DevOps pipeline but the Pulumi.AzureAD.AppRoleAssignment(s) get deleted and on the second attempt they get added back. So the principal has sufficient permissions to execute operations in the Azure AD. The log of Azure DevOps pipeline has these lines (Attempt #1): ~ azuread:index/application:Application: (update) <-- I did not make any changes for Azure AD Application Resources: using Pulumi;
using Pulumi.AzureAD.Inputs;
using Pulumi.AzureNative.Resources;
using Pulumi.AzureNative.Web;
using Pulumi.AzureNative.Web.Inputs;
using Pulumi.Random;
using AzureAD = Pulumi.AzureAD;
namespace EnterpriseSubscriber.Infrastructure;
public class Server
{
private const string AppType = "server";
public Server(string prefix, string environment, ResourceGroup resourceGroup, ServerServicePlan servicePlan)
{
var appName = $"app-qsr-{prefix}-{environment}";
var server = new WebApp(appName, new()
{
Name = appName,
ResourceGroupName = resourceGroup.Name,
ServerFarmId = servicePlan.Id,
Location = resourceGroup.Location,
HttpsOnly = true,
SiteConfig = new SiteConfigArgs
{
AlwaysOn = false,
AppSettings =
[
new NameValuePairArgs
{
Name = "ASPNETCORE_ENVIRONMENT",
Value = "Development"
}
],
NetFrameworkVersion = "v6.0",
HealthCheckPath = "/health"
},
Identity = new ManagedServiceIdentityArgs
{
Type = Pulumi.AzureNative.Web.ManagedServiceIdentityType.SystemAssigned
}
});
var todoReadRoleUuid = new RandomUuid($"uuid-{prefix}-todo-read-role-{AppType}-{environment}");
var todoWriteRoleUuid = new RandomUuid($"uuid-{prefix}-todo-write-role-{AppType}-{environment}");
var application = new AzureAD.Application($"appreg-{prefix}-{AppType}-{environment}", new AzureAD.ApplicationArgs
{
DisplayName = $"My Web API Server {environment}",
IdentifierUris =
{
$"api://{appName}-{AppType}"
},
Api = new ApplicationApiArgs
{
RequestedAccessTokenVersion = 2
},
SinglePageApplication = new ApplicationSinglePageApplicationArgs
{
RedirectUris =
{
Output.Format($"https://{server.DefaultHostName}/swagger/oauth2-redirect.html"),
"https://localhost:7167/swagger/oauth2-redirect.html"
}
},
AppRoles =
{
CreateRole(todoReadRoleUuid.Result, "Todo.Read"),
CreateRole(todoWriteRoleUuid.Result, "Todo.Write")
}
});
var principal = new AzureAD.ServicePrincipal($"sp-{prefix}-{AppType}-{environment}", new AzureAD.ServicePrincipalArgs
{
ClientId = application.ClientId
});
ApplicationClientId = application.ClientId;
ServicePrincipalObjectId = principal.ObjectId;
TodoReadRoleUuid = todoReadRoleUuid.Result;
TodoWriteRoleUuid = todoWriteRoleUuid.Result;
}
public Output<string> ApplicationClientId { get; private set; }
public Output<string> ServicePrincipalObjectId { get; set; }
public Output<string> TodoReadRoleUuid { get; set; }
public Output<string> TodoWriteRoleUuid { get; set; }
private static ApplicationAppRoleArgs CreateRole(Output<string> id, string name)
{
return new ApplicationAppRoleArgs
{
AllowedMemberTypes =
{
"Application"
},
DisplayName = name,
Enabled = true,
Value = name,
Description = name,
Id = id
};
}
private AzureAD.AppRoleAssignment AssignRead(string prefix, string environment, string assigneeName, Output<string> principalObjectId)
{
return new AzureAD.AppRoleAssignment(
$"appra-{prefix}-{assigneeName}-read-{AppType}-{environment}",
new AzureAD.AppRoleAssignmentArgs
{
AppRoleId = TodoReadRoleUuid,
PrincipalObjectId = principalObjectId,
ResourceObjectId = ServicePrincipalObjectId,
});
}
private AzureAD.AppRoleAssignment AssignWrite(string prefix, string environmnent, string assigneeName, Output<string> principalObjectId)
{
return new AzureAD.AppRoleAssignment(
$"appra-{prefix}-{assigneeName}-write-{AppType}-{environmnent}",
new AzureAD.AppRoleAssignmentArgs
{
AppRoleId = TodoWriteRoleUuid,
PrincipalObjectId = principalObjectId,
ResourceObjectId = ServicePrincipalObjectId
});
}
public void AssignRoles(string prefix, string assigneeName, string environment, Output<string> principalObjectId)
{
AssignRead(prefix, environment, assigneeName, principalObjectId);
AssignWrite(prefix, environment, assigneeName, principalObjectId);
}
public void AssignRoles(string prefix, string environment, ExternalClient client)
{
AssignRead(prefix, environment, client.AppName, client.PrincipalObjectId);
AssignWrite(prefix, environment, client.AppName, client.PrincipalObjectId);
}
} Here is my app pulumi stack: using Microsoft.Extensions.Options;
using Pulumi;
using Pulumi.AzureNative.Authorization;
using Pulumi.AzureNative.Resources;
using System;
namespace MyApp.Infrastructure;
public class MyAppStack : Stack
{
const string Prefix = "my-app";
public MyAppStack()
{
var environment = "Development";
var resourceGroupName = $"rg-{Prefix}-{environment}";
var resourceGroup = new ResourceGroup(resourceGroupName, new()
{
ResourceGroupName = resourceGroupName,
Location = location
});
var externalClient = new ExternalClient(Prefix, "Flowgear", environment);
var server = new Server(Prefix, environment, resourceGroup, serverServicePlan);
server.AssignRoles(Prefix, environment, externalClient);
}
} Here is the external client code: using Pulumi;
using AzureAD = Pulumi.AzureAD;
namespace MyApp.Infrastructure;
public class ExternalClient
{
private const string AppType = "external-client";
public ExternalClient(string prefix, string assigneeName, string environment)
{
AppName = $"{assigneeName.Replace(" ", "").ToLower()}-{AppType}";
var name = $"appreg-{prefix}-{AppName}-{environment}";
var application = new AzureAD.Application(name,
new AzureAD.ApplicationArgs
{
DisplayName = $"My App {assigneeName} {environment}",
Api = new AzureAD.Inputs.ApplicationApiArgs
{
RequestedAccessTokenVersion = 2
}
});
var applicationSecret = new AzureAD.ApplicationPassword($"apppwd-{prefix}-{AppName}-{environment}",
new AzureAD.ApplicationPasswordArgs
{
ApplicationId = application.Id
},
new CustomResourceOptions
{
AdditionalSecretOutputs =
{
"value"
}
});
var servicePrincipal = new AzureAD.ServicePrincipal($"sp-{prefix}-{AppName}-{environment}",
new AzureAD.ServicePrincipalArgs
{
ClientId = application.ClientId
});
ClientId = application.ClientId;
ClientSecret = applicationSecret.Value;
PrincipalObjectId = servicePrincipal.ObjectId;
}
public Output<string> ClientId { get; set; }
public Output<string> ClientSecret { get; set; }
public Output<string> PrincipalObjectId { get; set; }
public string AppName { get; set; }
} |
Thank you for the sample program! We'll take a deeper look when we can. |
I use MS Azure DevOps pipeline to deploy our web API and one of the tasks is to deploy IoC using Pulumi. I created customer application registration in Microsoft Entra ID using AzureAD.Application. I assigned roles to the app registration using AzureAD.AppRoleAssignment.
The issue is when I run the Azure DevOps pipeline again to deploy new code to production sometimes the roles disappear from the custom application registration in Microsoft Entra ID. I need to rerun the pipeline for the roles to reappear in the custom app registration. The infrastructure code was written in C# .NET 8.0. I did not make any changes to it.
Why do the roles (AzureAD.AppRoleAssignment) disappear from the custom app registration (AzureAD.Application)? Why do I need to rerun the pipeline for them to reappear? Is there a way to fix this?
Thank you very much.
The text was updated successfully, but these errors were encountered: