Open veikkoeeva opened 1 month ago
@veikkoeeva thanks for filing this issue. I don't think this is an issue with Graph Bicep. It looks like you are trying to create a new Azure role definition. However it looks like you are trying to add Entra permissions (as actions) to this role. Pretty sure that mixing and matching between Azure and Entra RBAC systems doesn't work. I don't think ARM knows anything about Entra (Microsoft.Directory) permissions.
Can you elaborate a bit more on what you are trying to do with this custom role definition? Are you trying to grant your service principal a set of application and service principal action permissions?
@dkershaw10 No problem for filing, thanks for asking more.
So, what I tried to do was/is:
Create bootsrap.bicep
that creates very basic structures such as management groups, subscriptions and a policy to restrict all workload identities that try to access Azure to a certain organization (OrganizationX
, see further). This Bicep script is run by a high privledged user from a command line like az deployment tenant create --name bootstrap --template-file ./main.bicep...
(could be stack deploy
, but Bicep Graph does not yet support it).
The bootstrap Bicep script main.bicep
creates subscriptions and management groups. It also creates one Workload identity and creates a policy that restricts all workload access to one organization and its repositories. Let's say this is https://github.com/OrganizationX/
. This workload identity grants access to one projects to deploy resources to Azure. Let's say this is https://github.com/OrganizationX/BaseProject
. These are high level resources such as policies and other things, created to management groups.
I was thinking that maybe the workload service principal granted to https://github.com/OrganizationX/BaseProject
could also create furher workload identities. Let's say there is project https://github.com/OrganizationX/RegularProject
that could have its own workload identity to access Azure, but it's severly more restricted and scoped e.g. to a resource group and so can do much less things.
But it appears that I cannot grant rolePermissions
to the to workload identity granted to https://github.com/OrganizationX/
so it could create application with 'Microsoft.Graph/applications
, then do Microsoft.Graph/servicePrincipals
and continue to give approriate, limited rolePermissions
that it is then given to https://github.com/OrganizationX/RegularProject
.
That can be done, but not through ARM APIs (or Azure role management).
You can grant those permissions to a service principal using the appRolesAssignedTo resource. You'd need to find the appRoleId for the Application.ReadWrite.OwnedBy
permission that you would grant using appRolesAssignedTo.
You can see an example here.
In terms of looking up the appRoleId, that's a bit trickier. You need to find the service principal that represents Microsoft Graph in your tenant (appId = 00000003-0000-0000-c000-000000000000), and look at that service principal's app roles to find the one with Application.ReadWrite.OwnedBy
to get its appRoleId.
That can be done, but not through ARM APIs (or Azure role management). You can grant those permissions to a service principal using the appRolesAssignedTo resource. You'd need to find the appRoleId for the
Application.ReadWrite.OwnedBy
permission that you would grant using appRolesAssignedTo.You can see an example here.
In terms of looking up the appRoleId, that's a bit trickier. You need to find the service principal that represents Microsoft Graph in your tenant (appId = 00000003-0000-0000-c000-000000000000), and look at that service principal's app roles to find the one with
Application.ReadWrite.OwnedBy
to get its appRoleId.
Thanks! OK, let I try to make further progress, maybe useful for others who read this too.
If I go to https://www.azadvertizer.net/azEntraIdAPIpermissionsAdvertizer.html?targetAppId=00000003-0000-0000-c000-000000000000 and put OwnedBy
as a term to Permission
, I see several identifiers. It may be what we're interested is https://www.azadvertizer.net/azEntraIdAPIpermissionsAdvertizer.html?targetPermissionId=18a4783c-866b-4cc7-a460-3d5e5662c884 (Application.ReadWrite.OwnedBy
)
Allows the app to create other applications, and fully manage those applications (read, update, update application secrets and delete), without a signed-in user. It cannot update any apps that it is not an owner of.
Taking this together with the example you shared and the code I shared in this thread, could it be like
resource symbolicname 'Microsoft.Graph/appRoleAssignedTo@v1.0' = {
appRoleId: '18a4783c-866b-4cc7-a460-3d5e5662c884' /* Probably would be neater to have this like a 'Bicep existing ='. */
principalId: /* If resourceId is the GH app SP, what could this be then be? The application created by the GH workload? */
resourceId: gitHubIdentityActionsServicePrincipal.id /* Is this right, the resource is the GitHub workload SP? */
}
I'm afraid I'm still taking some time here. I put the questions in the comments. I think the appRoleId
is found and its always the same for every tenant, so can be looked up like that.
But is resourceId
the GH workload SP that I create and assign to https://github.com/OrganizationX/BaseProject
and put that service principal to resourceId
is that right? In which I don't know what should be assigned to principalId
.
It appears I am confused as to why would I need two service principal identifiers instead of one and can't figure out how to cough up at least other one (which would allow also just to try out permutations).
No problem @veikkoeeva Yes you are on the right lines here.
The principalId
represents the principal that you want to grant the appRoleId to - in this case it would be the GitHub actions SP's ID.
The resourceId
represents the service principal ID of the resource/API that exposes the appRoleId - in this case it's Microsoft Graph.
I think the Bicep would look something like this (note I've not tested this E2E):
resource msGraphSP 'Microsoft.Graph/servicePrincipals@v1.0' existing = {
appId: '00000003-0000-0000-c000-000000000000'
}
// search for appRole by app role permission friendly name
param appRoleName string = 'Application.ReadWrite.OwnedBy'
var graphAppRoles = msGraphSP.appRoles
var appRoleDetail = filter(graphAppRoles, graphAppRoles => graphAppRoles.value == appRoleName)
resource symbolicname 'Microsoft.Graph/appRoleAssignedTo@v1.0' = {
appRoleId: appRoleDetail.id
principalId: gitHubIdentityActionsServicePrincipal.id // Client SP being granted permission to access the resource (API)
resourceId: maGraphSP.id // Resource here is Microsoft Graph
}
@veikkoeeva once you have your template(s) working, you are very welcome to contribute them to this repo here as an additional quickstart sample.
@veikkoeeva once you have your template(s) working, you are very welcome to contribute them to this repo here as an additional quickstart sample.
Sounds like a deal I try to do! I'm off-grid next week, so probably I try to deploy a minimal sample after next week and write a sample like those existing ones (maybe with a bit more comments :)).
@veikkoeeva LMK if you need any further help - glad to assist in any way.
@veikkoeeva LMK if you need any further help - glad to assist in any way.
I'm on it. I apologize for being so sluggish. I'm just a bit late after a week off from computer.
No worries or rush - I'm eager for the community to contribute samples, and hoping that your contribution might kickstart others to contribute too.
No worries or rush - I'm eager for the community to contribute samples, and hoping that your contribution might kickstart others to contribute too.
I want to worry a bit. I have a new small project in mind, better tie knots. I started a PR, but it's not ready yet. Mostly to show I got it started and if there's something that I should think already.
What I am thinking to write some more to README.md
and add also azadvertiser
resource with some explanations. Naturally I should test the script a bit too and document it. At the moment this indeed just that I put the pieces there. It's about midnight here now, I think I further this during the new few days so we hopefully get this over the finish line (knock, knock wood).
And sure, I'm pleased I can write and give back a bit. I think the issue is that people use a lot of time banging their head to the wall while solving problems. It would be awfully nice if a bit more were shared in problem-solution way and maybe a bit of reasoning and techniques to solve these kinds of issues. :)
I totally agree - I'm looking to restructure our template examples into proper "how-tos" that solve for real problems. My issue is that I can guess at some of these things, but requires real input from customers who are facing these problems. I might put out a call to action at the next Bicep community call, to ask folks for a short write-up of some of their real world problems in this space, and see if we can pivot towards more useful template examples that solve for these problems (or identify gaps we have).
@dkershaw10 My general feeling of the examples here is:
Could use more explanations. F.ex. your iterator to find the identifier is a good one, I think adding that link to a comment in the code improves more about finding and inspecting values. That is, giving techniques to find help on one's own, teaching to fish. :)
The templates are by necessity short and focused. I was working on this several hours. Some real issues:
az deploy
. Why did I use stack first? Well, since you were so helpful, I thought I make use of this and used existing process and make it work. That way I also uncover issues even if the environment is "very sanitized".for usage details.","details":[{"code":"","message":"{\"error\":{\"code\":\"Forbidden\",\"target\":\"/resources/githubActionsApplication\",\"message\":\"Insufficient privileges to complete the operation. Graph client request id: c252b038-26e8-4b5a-917f-052bd3a8ba07. Graph request timestamp: 2024-08-05T21:01:58Z.\"}}"}]}}
Now, that list one may be a propagation issue too. I did try it many times over a span of time almost an hour. I also went to Azure portal to explore and saw it adviced to check some issues with it. I did that, but it didn't work. Then from the portal I added the rights to manage all resources (bad, I know) and set up a daily schedule to run this Bicep. Since I went to sleep and though that GitHub retries on my behalf. Now the latest issue in fact is:
The client 'guid' with object id 'same guid as previous' does not have permission to perform action 'Microsoft.Authorization/roleDefinitions/write' at scope '
Which looks like progress to me. I'm fairly sure everyone wants to set up a role definition with this, so further rights need to given for this FDIC (that was created by the bootstrap thas has the Graph rights) that creates other FDICs for projects and assigns roles for them. So, not only other per-repository Graph resources but also assigns appropriate roles to them.
I consider if the URL to portal looks stable and/or adding a screenshot would be useful to help people looking at a sample like this. Then also it likely will be useful to to have some scripts lile az ad app permission list --id someId
or even more complex like az role assignment list --scope "/providers/Microsoft.Management/managementGroups/some-mg-group" --query "[].{PrincipalId:principalId, RoleDefinitionName:roleDefinitionName}" -o json
to show how to check some things. I don't remember this, I'm not sure if ChatGPT can always write one to help.
This is a bit of a self-study (probably helps with cert exams :)) so after a day of work, I think, I remove the apps, SPs etc. from Azure and re-run the bootstrap PS script with my high level privileges to create the root FDIC that is assigned to this GitHub repository, with the appropriate permissions to create new FDICs and assign role sto them. I screenshot the Azure portal image and paste here (if not for other things but to give and idea for readers who come later) and try to figure what priviledges it really needs to create new FDICs and assign roles.
As an aside, is there a good way to check what are sufficient privileges when the error message is that they were insufficient? It's trial-and-error for me and I suspect for many others too. I do see MS docs are being cleaned at the moment to not run all the examples with high privileges but what appears to be more limited or maybe even minimum ones, which is a good thing.
I hope not too messy. I wrote this quickly on a morning coffee to also leave some notes what happens when your random developer gets an idea to do something (and why e.g. MS Aspire might be popular). :)
@veikkoeeva Firstly - thank you for all the effort you are putting in here to develop a high quality sample, and set a great example for everyone else - plus all your thinking here.
I will review your PR (and ask some engineers on our team to take a look too), to provide feedback. A couple of points and thoughts for you:
@veikkoeeva In terms of permissions required to run the template - assuming that you are doing this with a signed-in user and using Az CLI/Az PS:
This Entra built-in roles topic should help you. with item 2.
Both these roles are highly privileged roles, and I would recommend the use of Privileged Identity Management here to temporarily provide these roles, to run this template.
@dkershaw10
Firstly - thank you for all the effort you are putting in here to develop a high quality sample, and set a great example for everyone else - plus all your thinking here.
No problem. I think at mimimum I need to get this to work for myself reliably so I can document it for others. Takes a bit of time as I need to arrange time to spend time with Azure (removing tokens, re-creating, re-running GitHub to check etc.).
For creating app role assignments to Graph app roles requires the additional **Privileged Role Administrator" role.
So, in order for the FDIC application on GitHub to create other FDICs it needs something else than Application.ReadWrite.OwnedBy
?
I ask since I'm not sure what you mean. I assume so.
Both these roles are highly privileged roles, and I would recommend the use of Privileged Identity Management here to temporarily provide these roles, to run this template.
The idea I have had with this is that I can run a bootstrap script to create a FDIC with sufficient privileges to create others and restrict it then to a given repository and audit via PRs and roles what it can do and where. It consumes a Bicep script and creates FDICs for other projects/repositories. So the idea is to make this process more auditable and constrained like this.
Thanks for the comments on the PR. I'll fix those while working with this.
@veikkoeeva Let me try this again. To avoid confusion I think there are 3 things here:
The bootstrap script creates the "creator" app.
The "creator app" creates the apps in 3, based on Bicep templates - this uses CI/CD practises.
I think the sample you have is for item 1 - to create a "creator" app that can create other apps To interactively run bootstrap.bicep, the signed-in user must be in a privileged role to deploy a declared "creator" app, SP and federated identity credential and assign that creator app the app-only permissions it needs to be able to create apps (number 3). So to run bootstrap.bicep interactively using Az CLI/PS, the signed-in user needs the Entra Application Administrator and Privileged Role Administrator roles.
Does this make sense?
Questions
I think the sample you have is for item 1 - to create a "creator" app that can create other apps To interactively run bootstrap.bicep, the signed-in user must be in a privileged role to deploy a declared "creator" app, SP and federated identity credential and assign that creator app the app-only permissions it needs to be able to create apps (number 3). So to run bootstrap.bicep interactively using Az CLI/PS, the signed-in user needs the Entra Application Administrator and Privileged Role Administrator roles.
Does this make sense?
Yes. This is what I do. I have a PowerShell script that runs a Bicep script that creates the initial "creator FDIC" and an application and sets up the parameters application and FDIC parameters to creator
GitHub repository and e.g. assigns a role to this FDIC that that restrict only from that company account. So, this phase is run by my own, high level privileges.
The problem I described happend when I run a GitHub pipeline in creator
repository where I want it can create a FDIC to project SomeProject
using another Bicep. So, here I use the "creator FDIC" in the GitHub pipeline to deploy a Bicep template to Azure . I got that error Insufficient privileges to complete the operation. Graph client request id...
in the GitHub pipeline in creator
repository.
I plan to spend some time this evening to continue where I left off last time. As noted I just pumped from Azure portal what I considered was more priviledges and let a timer to run the script. Next morning I saw the error had changed to that "Microsoft.Authorization/roleDefinitions/write' at scope". In the Bicep script creator
tries to deploy when it creates the FDIC to "SomeProject" there are also roles it tries to assign to that FDIC. The error looked to me the creator
FDIC wasn't authorized to do that. So, this looks like being something I need to allow it to do in the initial bootstrap script.
@veikkoeeva Gotcha - we have a known issue that we are trying to get to the bottom of, and fix. The expectation is that an SP with the Application.ReadWrite.OwnedBy
permission is set as the owner of any application it creates. That is not happening. So any subsequent operations to either add FIC or create an SP from the creator
are failing with the 403. You can see some discussion about this in #142.
The only workaround (that doesn't involve manually updating the ownership on the created application) would be to give the creator
app the Application.ReadWrite.All
permission. This is a much more privileged permission that will allow the creator
app the ability to create apps/SPs and update any app or SP in the tenant. It's probably fine to use this and verify the flow end to end, but I wouldn't recommend deploying the creator
app to production with this privileged permission.
I plan to spend some time this evening to continue where I left off last time. As noted I just pumped from Azure portal what I considered was more priviledges and let a timer to run the script. Next morning I saw the error had changed to that "Microsoft.Authorization/roleDefinitions/write' at scope". In the Bicep script creator tries to deploy when it creates the FDIC to "SomeProject" there are also roles it tries to assign to that FDIC. The error looked to me the creator FDIC wasn't authorized to do that. So, this looks like being something I need to allow it to do in the initial bootstrap script.
Right - this was my second question - because as I think you've figured out, the creator
SP needs to be assigned to some Azure RBAC role/permission that allows it to assign Azure roles to the workload identities it creates.
cc: @eketo-msft
Bicep version
Resource and API version Which Microsoft.Graph resource and API version has the issue?
Microsoft.Graph/applications@v1.0
Auth flow User signed to deploy a Bicep script from a command line using
az deployment create ...
.Deployment details I can provide if it makes sense.
Describe the bug A clear and concise description of what the bug is vs what you expected to happen
I am using the new Bicep Graph module to create a workload identity that can then be assigned to e.g. GitHub to a certain repository. The purpose is to assign further workload identities from this repository that are defined in Bicep files there.
When I try to deploy a
service principal
with a role that I think has some of the rightassignableScopes
, I get an errorIt feels to me I should be able do to this in Bicep and just put wrong
assignableScopes
. But on the other hand there's probably more into this. This may be related to https://github.com/microsoftgraph/msgraph-bicep-types/issues/134. I am not sure if this is a bug or would be a feature. It may also very well be I don't know just how to make this happen in Bicep. :)To Reproduce Additional context
The Bicep I use are like follows. I have permuted all kinds of things to
assignableScopes
, but as expected, the error message stays the same.