MS graph api: Subscribe for notifications on groups changes with Azure event hub - azure

basically I am trying to subscribe for notifications on group changes in order to adjust authorizations in a 3rd party system, find the code below. It uses the graph sdk for Java. I have added the documentation I followed for reference, see Change notification delivery and post subscriptions in Microsoft Docs.
Unfortunately I get a
Invalid event hub notification url. I tried both domain and tenant id, no luck. It doesn't really surprise me as the notificationUrl really seems odd. Can anyone share some light in here?
// From https://learn.microsoft.com/de-de/graph/change-notifications-delivery:
// The main difference during subscription creation will be the notificationUrl. You must set it to
// EventHub:https://<azurekeyvaultname>.vault.azure.net/secrets/<secretname>?tenantId=<domainname>, with the following values:
// azurekeyvaultname - The name you gave to the key vault when you created it. Can be found in the DNS name.
// secretname - The name you gave to the secret when you created it. Can be found on the Azure Key Vault Secrets page.
// domainname - The name of your tenant; for example, consto.onmicrosoft.com or contoso.com. Because this domain will be used to access the Azure Key Vault, it is important that it matches the domain used by the Azure subscription that holds the Azure Key Vault. To get this information, you can go to the overview page of the Azure Key Vault you created and click the subscription. The domain name is displayed under the Directory field.
#GetMapping("/subscribe")
public void subscribeTochangeNotifications() {
// following https://learn.microsoft.com/en-us/graph/api/subscription-post-subscriptions?view=graph-rest-1.0&tabs=http#request-example
Subscription subscription = new Subscription();
subscription.changeType = "created,updated";
subscription.notificationUrl = "EventHub:https://xxxxxxxxx.vault.azure.net/secrets/event-hub-client-secret?tenantId=xxxxxxxxx-xxxx-xxxx-xxxxxxxxx";
subscription.expirationDateTime = OffsetDateTime.parse("2022-07-05T18:23:45.9356913Z");
subscription.resource = "/groups";
subscription.clientState = "SecretClientState";
azureClient.subscriptions().buildRequest().post(subscription);
}
Detailed error message is:
nested exception is com.microsoft.graph.http.GraphServiceException: Error code: InvalidRequest
Error message: Invalid event hub notification url='EventHub:https://xxxxxxxxxxxxxxxxx.vault.azure.net/secrets/event-hub-client-secret?tenantId=yyyyyyy-yyy-yyyy-yyyyyyyyyy'.
POST https://graph.microsoft.com/v1.0/subscriptions
SdkVersion : graph-java/v5.30.0
SdkVersion : graph-java/v5.30.0
[...]
400 : Bad Request
[...]

we ran into the same issue (setup with pulumi). Our connection string in the key vault secret was missing the
";EntityPath=graphevents"
at the end.

Related

Azure Key Vault: The user, group, or app does not have secrets set permission on key vault

I am creating a script using Azure CLI that will automatically generate an App Registration (service principal), and then use that App Registration to create a secret that will be stored in Azure Key Vault.
However, I am getting the following error:
The user, group or application 'appid=04b07795-8ddb-461a-bbee-02f9e1bf7b46;oid=0ec2b0e8-daeb-46a8-b627-0d4f61f87157;numgroups=134;iss=https://sts.windows.net/72f988bf-86f1-41af-91ab-2d7cd011db47/' does not have secrets set permission on key vault 'asakeyabcfelaqpgsfnxcy;location=eastus'. For help resolving this issue, please see https://go.microsoft.com/fwlink/?linkid=2125287
Can anyone provide guidance on what this ID is and how to resolve this error? This is not my App Registration Object ID or App ID.
I think there're 2 points you're now concerning, one is you failed to add secret, another is the app id in the error message is not the one you registered.
I think you've followed the document to execute the cli command, so I just want to explain the second point. Pls allow me show you a code sample for a better explanation.
using Azure.Identity;
using Azure.Security.KeyVault.Secrets;
public async Task<IActionResult> PrivacyAsync()
{
var kvUri = "https://your_vault_name.vault.azure.net/";
var client = new SecretClient(new Uri(kvUri), new DefaultAzureCredential());
_ = await client.SetSecretAsync("test0311", "hello");
return View();
}
When we want to add key vault secret to azure, we need to provide a credential so that our operations are authenticated. This is the DefaultAzureCredential() here, and it has several sources to get the authentication like screenshot below.
That means if someone sets the environment variables for authentication, then it will cover the information you entered when executing cli command, this may usually cause the issue that the app is different from what you set. I think you may follow this document to check all your configurations and try again, or you can directly add environment variables with the app you registered on your computer.
By the way, pls don't forget to add access policy in azure portal for the azure ad app you registered.

Microsoft Graph error processing a storage extension white getting subscription by ID

I am trying to get a subscription by ID from Microsoft Graph REST. If I list the subscriptions using GET /subscriptions, things are working fine and I am able to list all subscriptions. But if I take a subscription id from the list and try to get the details using GET /subscriptions/{id}, I am getting the following error:
{
"error":
{
"code":"ExtensionError",
"message":"There was an error processing a storage extension.",
"innerError":
{
"date":"2021-10-12T06:59:47",
"request-id":"ffa4f181-148d-49ed-8c9d-f551f6ddd6f0",
"client-request-id":"ffa4f181-148d-49ed-8c9d-f551f6ddd6f0"
}
}
}
Any ideas on where I might be going wrong?
PS: I am trying to get an individual subscription because the /subscriptions route does not provide the clientState property which is required for me.
When I route to the same /subscription got the #odata.context link.
I open the same link and search for clientState and got its type but not its value.
Note: the clientState property value is not returned for security purposes.
refer this github
Note : Client State is not set for the List Subscription on purpose by design, because subscription collections could be listed by say some other user (example tenant admin using Subscription.Read.All). It is not desirable to share this Client state information through the Listing API for that user who is not the creator of the subscription.
Reference : https://github.com/microsoftgraph/microsoft-graph-docs/issues/5248

How to create a google cloud pubsub pull subscriptions with service account by terraform?

In the terraform documentation for google_pubsub_subscription, it mentions having a oidc_token property under push_configuration allows push-subscriptions to utilise service account, but has no mention on how to use service account for pull subscriptions.
How can explicitly set the service account to be used during the creation of pull pubsub subscriptions?
I tried adding oidc_token block, but it did not worked as it does not expect that block directly.
Scenario:
I have a service account that has access to pubsub topics (and necessary permissions to attach subscriptions to it) in Project_A
I want to create subscriptions to those topics in Project_B in terraform.
I need to explicitly use this service account in terraform, so i can create subscriptions at Project_B to topics of Project_A
google_cloudfunctions_function resource for example, has field called service_account_email for setting service account. But there is no for google_pubsub_subscription resource, for pull subscriptions in the documentation.
Actually the service account (service_account_email)is to be specified inside the oidc_token section (you had to go further in the doc :)
below a working example of a push sub with service account and audience (optional)
variable "project" {
type = string
default = "<YOUR_PROJECT_ID>"
}
resource "google_pubsub_topic" "example" {
project = var.project
name = "example-topic"
}
resource "google_pubsub_subscription" "example" {
project = var.project
name = "example-push-subscription-with-auth"
topic = google_pubsub_topic.example.name
push_config {
push_endpoint = "https://example.com/push"
oidc_token {
service_account_email = "${var.project}#appspot.gserviceaccount.com"
audience = "https://example.com/push"
}
}
}
I'm adding another answer, because the question has changes a lot after the different comments.
Here is the solution with the hypothesis that a topic topic-sof is already created in a different project than the subscription one.
I created a service account (SA) on the subscription project, I called it stackoverflow-playground-sa and gave it only the Pub/Sub Editor role as you can see in the screenshot below.
I gave the SA the the Pub/Sub Subscriber role on the topic topic-sof s show in the screenshot below.
If you don't do this step: you will get this error at terraform apply
Error: Error creating Subscription: googleapi: Error 403: User not authorized to perform this action
Of course I did the 2 first steps of roles assignments with a user with enough permissions on both projects
I created a json key file for my SA and downloaded under /path/to/my/key/stackoverflow-playground-sa.json
I authenticated as my SA using
export GOOGLE_APPLICATION_CREDENTIALS=/path/to/my/key/stackoverflow-playground-sa.json
So it can be used by terraform to create the subscription.
Here is the terraform configuration to create the subscription
variable "subscription_project" {
type = string
default = "<YOUR_SUBSCRIPTION_PROJECT>"
}
variable "topic_project" {
type = string
default = "<YOUR_TOPIC_PROJECT>"
}
resource "google_pubsub_subscription" "pull-subscription-of-topic-in-another-project" {
project = var.subscription_project
name = "pull-subscription-of-topic-in-another-project"
topic = "projects/${var.topic_project}/topics/topic-sof"
}
Run (apply) my terraform and the subscription pull-subscription-of-topic-in-another-project is created and attached to the topic topic-sof in the other project.
I published a message to topic-sof using the web ui.
Still authenticated as stackoverflow-playground-sa thanks to step 4, I pull the message (using gcloud in my treminal) et voilĂ  message received:
To summarise : there is no service account to specify in your terraform configuration for this requirement. The service account and its key is set outside terraform (step 1 to 4) so that terraform process can be authenticated and authorised to create the resources configured (the subscription in our case).
Besides using a service account key as in step 4 is a bad practice, security wise. An alternative is to use
gcloud auth application-default login
That will let you set the default credentials of the user of your choice. Given that user has the roles I set for the SA.

Azure Service Bus managed identity in Visual Studio returning 401 - Token issuer is invalid

I'm attempting to access Azure Service Bus using a managed identity from my code. At the moment I'm just trying this locally.
When I debug my code I get the following error
System.UnauthorizedAccessException: Put token failed. status-code: 401, status-description: InvalidIssuer: Token issuer is invalid
Here is my service bus instance
Here is my user with Azure Service Bus Data Owner permissions
And here is my code
_client = new ServiceBusClient("oconnorevents.servicebus.windows.net", new DefaultAzureCredential());
I am logged into Visual Studio as the same user added to the service bus. I also tried logging in via the CLI but it didn't help.
Where am I going wrong here?
I've looked at this similar recent question here but the solutions proposed didn't work for me.
Since I have access to several different tenants, Visual Studio sometimes gets confused. Another way you can handle this is to continue to use the DefaultAzureCredential, but to give Visual Studio a hint about which tenant to use.
First left click the your project and examine the properties and then:
Left-click "Debug"
Left-click the "Add" button to add an environment variable
For name use "AZURE_TENANT_ID" and for value use your tenant id. Yes, that is a bogus tenant id in the picture :-)
Reference
https://learn.microsoft.com/en-us/dotnet/api/azure.identity.environmentcredential?view=azure-dotnet
https://damienbod.com/2020/10/09/using-key-vault-certificates-with-microsoft-identity-web-and-asp-net-core-applications/
If you use DefaultAzureCredential to auth, it will try several credential types to auth as mentioned here, one of them is VisualStudioCredential, but it will auth to the home AAD tenant of the user logged in VS, in your case, I suppose the service bus is in a subscription which is not under the home tenant of the user.
I can also reproduce your issue on my side.
To solve the issue, just use VisualStudioCredential directly, then simply specify the TenantId via VisualStudioCredentialOptions, then it will work fine.
Sample:
To find the TenantId, just navigate to the Azure Active Directory which the subscription of your service bus located.
TokenCredential tokenCredential = new VisualStudioCredential(new VisualStudioCredentialOptions {TenantId = "xxxxxxx" });
ServiceBusClient client = new ServiceBusClient("xxx.servicebus.windows.net", tokenCredential);
Specify the exact tenant id by adding the following key to local.settings.json.
"AZURE_TENANT_ID": "your tenant id"
I tried to create an azure function that receives messages from a service bus queue using a managed identity trigger and it worked for me.
late to the party but I got it working on my local Visual Studio with this code
var tokenCredential = new VisualStudioCredential(new VisualStudioCredentialOptions { TenantId = "xxx-xxx" });
ServiceBusClient client = new ServiceBusClient("my-name-space.servicebus.windows.net", tokenCredential);
sender = client.CreateSender('my-topic');
var msgBody = new Person{ Name = 'joe'};
await sender.SendMessageAsync(new ServiceBusMessage(JsonConvert.SerializeObject(msgBody)));
Also, remember to sign in to Azure in your Visual Studio,
and assign your account to the role "Azure Service bus Data Sender" , see below:

Access denied linking variable group to key vault in VSTS

When I try to link a variable group to key vault in VSTS, every time I select my endpoint, it tells me:
"Specified Azure endpoint needs to have "Get, List" secret management permissions on the selected key vault. Click "Authorize" to enable VSTS to set these permissions or manage secret permissions in Azure portal."
It makes no sense. That specific endpoint has Get and List for secrets, keys, certificates. The endpoint is using the correct Service Principal Client ID- I know because I pulled its corresponding app registration up in Azure by searching with that ID. And the app registration is specifically listed in the key vault's Access policies, with the correct permissions.
Trying to click "Authorize" in VSTS just give me
"Resource not found for the segment 'DirectoryDataService.getServicePrincipalsByAppIds'. For troubleshooting refer to https://go.microsoft.com/fwlink/?linkid=835898"
and the endpoint is broken until I re-verify it.
I'm kind of at my wit's end here- everything is set exactly as https://learn.microsoft.com/en-us/vsts/build-release/concepts/library/variable-groups?view=vsts says it should be.
Edit: Turns out the Azure tried to add the app reg as a person instead of an app when I listed it in the ARM template by object ID (app ID most definitely does not work there). So now I just need to figure out how to add it as an application in the ARM template...
Edit Edit: Soooo... the Object ID the Azure Portal shows for an App Registration? That's not the Object ID the ARM template wants. It wants the... I'm not sure what you'd call it. In Powershell, you use it with -Object ID, but when you list the properties, it's under "Id". Whatever. To get it, you run
Get-AzureRmADServicePrincipal -SearchString "[your-app-reg-name]"
And it shows up under Id. That's what you want to use as an object ID in your ARM template.
According to the error Resource not found for the segment 'DirectoryDataService.getServicePrincipalsByAppIds, the issue seems more related to Azure side.
To access azure-keyvault you need four things :
- clientId = "<client id of your application registed on Azure AD>";
- domain = "<your talnet id>";
- secret = "<client key of your application registed on Azure AD>";
- subscription = "<your subscription id>";
Then these will combine ApplicationTokenCredentials, finally Authorize to KeyVaultClient . Suggest you take a look at this question: Azure keyvault client 1.0.0 initiate client
Besides also double check/confirm the specific endpoint has Get and List for secrets, keys, certificates.

Resources