New-StreamAnalyticsJob cannot create Operations Monitoring Input for an IOT Hub - azure-powershell

We have a Stream Analytics job that has an Input mapping to an IOT Hub Operations Monitoring endpoint. We originally defined our job on the Azure Portal. It works fine when so created / updated.
We use the job logic in multiple "Azure environments" and are now keeping it in source control. We used the Visual Studio Stream Analytics Project type to manage the source code.
We are using the New-StreamAnalyticsJob Powershell command to deploy our job into different environments.
Each time we deploy, however, the resulting Stream Analytics Job's Input points to the Messaging endpoint of our IOT Hub instead of the Operations Monitoring endpoint.
Is there something we can enter into the input's JSON file to express the endpoint type? Here is the Input content of our JSON input to the cmdlet:
"Inputs": [{
"Name": "IOT-Hub-Monitoring-By-Consumer-Group",
"Properties": {
"DataSource": {
"Properties": {
"ConsumerGroupName": "theConsumerGroup",
"IotHubNamespace": "theIotNamespace",
"SharedAccessPolicyKey": null,
"SharedAccessPolicyName": "iothubowner"
},
"Type": "Microsoft.Devices/IotHubs"
},
"Serialization": {
"Properties": {
"Encoding": "UTF8",
"Format": "LineSeparated"
},
"Type": "Json"
},
"Type": "Stream"
}
},
{
"Name": "IOT-Hub-Messaging-By-Consumer-Group",
"Properties": {
"DataSource": {
"Properties": {
"ConsumerGroupName": "anotherConsumerGroup",
"IotHubNamespace": "theIotNamespace",
"SharedAccessPolicyKey": null,
"SharedAccessPolicyName": "iothubowner"
},
"Type": "Microsoft.Devices/IotHubs"
},
"Serialization": {
"Properties": {
"Encoding": "UTF8",
"Format": "LineSeparated"
},
"Type": "Json"
},
"Type": "Stream"
}
}
]
Is there an endpoint element within the IotHubProperties that we're not expressing? Is it documented somewhere?

I notice that the Azure Portal calls a different endpoint than is indicated here: https://learn.microsoft.com/en-us/rest/api/streamanalytics/stream-analytics-definition
It uses endpoints under https://main.streamanalytics.ext.azure.com/api. e.g.
GET /api/Jobs/GetStreamingJob?subscriptionId={guid}&resourceGroupName=MyRG&jobName=MyJobName
You'll notice in the results JSON:
{
"properties": {
"inputs": {
{
"properties": {
"datasource": {
"inputIotHubSource": {
"iotHubNamespace":"HeliosIOTHubDev",
"sharedAccessPolicyName":"iothubowner",
"sharedAccessPolicyKey":null,
---> "endpoint":"messages/events", <---
"consumerGroupName":"devicehealthmonitoring"
}
For operations monitoring you will see "endpoint":"messages/operationsMonitoringEvents"
They seem to implement Save for Inputs as PATCH /api/Inputs/PatchInput?... which takes a similarly constructed JSON with the same 2 values for endpoint.
Are you able to use that endpoint somehow? i.e. call New-AzureRmStreamAnalyticsJob as you normally would then Invoke-WebRequest -Method Patch -Uri ...
--Edit--
The Invoke-WebRequest was a no-go -- far too much authentication to try to replicate/emulate.
A better option is to go through this tutorial to create a console application and set the endpoint after deploying using the Powershell scripts.
Something like this should work (albeit with absolutely no error/null checks):
string tenantId = "..."; //Tenant Id Guid
string subscriptionId = "..."; //Subcription Id Guid
string rgName = "..."; //Name of Resource Group
string jobName = "..."; //Name of Stream Analytics Job
string inputName = "..."; //Name-of-Input-requiring-operations-monitoring
string accesskey = "..."; //Shared Access Key for the IoT Hub
var login = new ServicePrincipalLoginInformation();
login.ClientId = "..."; //Client / Application Id for AD Service Principal (from tutorial)
login.ClientSecret = "..."; //Password for AD Service Principal (from tutorial)
var environment = new AzureEnvironment
{
AuthenticationEndpoint = "https://login.windows.net/",
GraphEndpoint = "https://graph.windows.net/",
ManagementEnpoint = "https://management.core.windows.net/",
ResourceManagerEndpoint = "https://management.azure.com/",
};
var credentials = new AzureCredentials(login, tenantId, environment)
.WithDefaultSubscription(subscriptionId);
var azure = Azure
.Configure()
.WithLogLevel(HttpLoggingDelegatingHandler.Level.Basic)
.Authenticate(credentials)
.WithDefaultSubscription();
var client = new StreamAnalyticsManagementClient(credentials);
client.SubscriptionId = azure.SubscriptionId;
var job = client.StreamingJobs.List(expand: "inputs").Where(j => j.Name == jobName).FirstOrDefault();
var input = job.Inputs.Where(i => i.Name == inputName).FirstOrDefault();
var props = input.Properties as StreamInputProperties;
var ds = props.Datasource as IoTHubStreamInputDataSource;
ds.Endpoint = "messages/operationsMonitoringEvents";
ds.SharedAccessPolicyKey = accesskey;
client.Inputs.CreateOrReplace(input, rgName, jobName, inputName);

The suggestion from #DaveMontgomery was a good one but turned out to be not needed.
A simple CMDLET upgrade addressed the issue.
The root issue turned out to be that the Azure Powershell Cmdlets, up to and including version 4.1.x were using an older version of the Microsoft.Azure.Management.StreamAnalytics assembly, namely 1.0. Version 2.0 of Microsoft.Azure.Management.StreamAnalyticscame out some months ago and that release included, as I understand, adding an endpoint element to the Inputs JSON structure.
The new CMDLETs release is documented here: https://github.com/Azure/azure-powershell/releases/tag/v4.2.0-July2017. The commits for the release included https://github.com/Azure/azure-powershell/commit/0c00632aa8f767e58077e966c04bb6fc505da1ef, which upgrades to Microsoft.Azure.Management.StreamAnalytics v2.0.
Note that this was a beaking change, in that the JSON changed from PascalCase to camelCase.
With this change in hand we can add an endpoint element to the Properties / DataSource /Properties IOT input, and the as-deployed Stream Analytics Jobs contains an IOT Input properly sewn to the operationsMonitoring endpoint.

Related

Attempting to subscribe to a Shopify Webhook w/AWS EventBridge produces error: "Address is an AWS ARN and includes api_client_id 'x' instead of 'y'"

I'm running this request through Postman. Some posts to the Shopify developer forum (e.g., this one) express without clear explanation that the request should be made within the Shopify app that would be subscribing to the Webhooks, but Postman seems to work, too.
In Postman . . .
Here's the endpoint:
https://{{shopifyDevelopmentStoreName}}.myshopify.com/admin/api/2022-07/graphql.json
Here's the GraphQL body:
mutation createWebhookSubscription($topic: WebhookSubscriptionTopic!, $webhookSubscription: EventBridgeWebhookSubscriptionInput!) {
eventBridgeWebhookSubscriptionCreate(
topic: $topic,
webhookSubscription: $webhookSubscription
) {
webhookSubscription {
id
}
userErrors {
message
}
}
}
Here's the payload being sent (notice the "client_id_x" value within the arn property):
{
"topic": "PRODUCTS_CREATE",
"webhookSubscription": {
"arn": "arn:aws:events:us-east-1::event-source/aws.partner/shopify.com/client_id_x/LovecraftEventBridgeSource",
"format": "JSON",
"includeFields": "id"
}
}
Here's the response I receive:
{
"data": {
"eventBridgeWebhookSubscriptionCreate": {
"webhookSubscription": null,
"userErrors": [
{
"message": "Address is invalid"
},
{
"message": "Address is an AWS ARN and includes api_client_id 'client_id_x' instead of 'client_id_y'"
}
]
}
},
"extensions": {
"cost": {
"requestedQueryCost": 10,
"actualQueryCost": 10,
"throttleStatus": {
"maximumAvailable": 1000.0,
"currentlyAvailable": 990,
"restoreRate": 50.0
}
}
}
}
What's entirely unclear is why Shopify is insisting upon validity of "client_id_y" when, in AWS, the value being displayed is undeniably 'client_id_x'. Extremely confusing. I don't even see what difference using the Shopify app would make except that it produces a client_id value that works counter to one's expectations and intuitions.
Does anyone know why the heck Shopify isn't just using the client_id value of the event bus created earlier in Amazon EventBridge?
Same happend to me and I was lucky to find a solution.
The error message is just missleading.
I replaced the API Access Token for the Shopify Rest API Request (X-Shopify-Access-Token)
with the one from the Shopify App holding the aws credentials.
admin/settings/apps/development -> app -> API credentials -> Admin API access token. (can only be seen after creation)
Then I could subscribe webhooks to the app via the Rest Interface.

How to set PartitionKey property from stream analytics on the output message to service bus?

I have following setup:
Event hub
Service bus topic with a single subscription that accepts all messages from the topic (for my POC)
Above service bus subscription is setup with sessions enabled
Stream analytics (SA) job that moves the events from event hub (the input) into service bus topic (the output). Here is my SA query:
SELECT *, LOWER(source) as Partner
INTO [sb-output]
FROM [test-input]
The above job also sets the partition key for service bus. Used following json in the System Properties of [sb-output] based on the documentation at https://learn.microsoft.com/en-us/azure/stream-analytics/service-bus-topics-output#custom-metadata-properties-for-output:
{ "PartitionKey" : "Partner" }
What I did:
Sent an event to event hub without partition key. This was successful.
{
"specversion": "1.0",
"id": "c8c4faad-9f53-4e43-95ca-c318d673660a",
"type": "CustomerChanged",
"time": "2020-09-09T22:25:40.0148301Z",
"source": "ABCD",
"subject": "system-1",
"datacontenttype": "application/json",
"dataschema": "1.0",
"data": {
"customerNumber": "7879875212123",
"firstName": "John",
"lastName" : "Kennedy"
}
}
The SA successfully moved the event from event hub to service bus.
The service bus subscription succcessfully received the message as shown below:
{
"specversion": "1.0",
"id": "c8c4faad-9f53-4e43-95ca-c318d673660a",
"type": "CustomerChanged",
"time": "2020-09-09T23:22:13.3647825Z",
"source": "ABCD",
"subject": "system-1",
"datacontenttype": "application/json",
"dataschema": "1.0",
"data": {
"customerNumber": "7879875212123",
"firstName": "John",
"lastName": "Kennedy"
},
"EventProcessedUtcTime": "2020-09-09T23:22:14.3776603Z",
"PartitionId": 0,
"EventEnqueuedUtcTime": "2020-09-09T23:22:14.3080000Z",
"Partner": "abcd"
}
As can be seen, the property Partner is at the end of the message.
However the Service Bus Explorer tool shows me that the PartitionKey property has not been set to "abcd" but to some other random string.
Troubleshooting:
To make sure that I can send a message to service bus topic with a specific PartitionKey key, I wrote a sample code that submits the message to service bus topic by explicitly setting the session id property on the message. The service bus explorer showed me that both SessionId as well as PartitionKey properties are set to the correct value.
In ASA output configuration, tried to set following system properties json. Neither worked.
{ "SessionId" : "Partner" }
{ "PartitionKey" : "Partner", "SessionId" : "Partner" }
In ASA output configuration, tried setting both Property Columns (to Partner) as well as System Property Columns (to { "PartitionKey" : "Partner" }). That did not work.
Question:
What am I doing wrong with ASA output configuration that is circumventing the propagation of PartitionKey value from my custom field to service bus message?
Also is there a reason why the System Property Columns does not show the json text that I entered after saving?
Sharing the answer as per the comment by the original poster:
Currently, Microsoft is working on a fix.
Until then, you can use the suggested work around: { "PartitionKey" : "Partner", "SessionId" : "Partner", "Label": "Partner" } in system properties.

Extracting custom objects from HttpContext

Context
I am rewriting an ASP.NET Core application from being ran on lambda to run on an ECS Container. Lambda supports the claims injected from Cognito Authorizer out of the box, but Kestrel doesn't.
API requests are coming in through API Gateway, where a Cognito User Pool authorizer is validating the OAuth2 tokens and enriching the claims from the token to the httpContext.
Originally the app was running on lambda where the entry point was inheriting Amazon.Lambda.AspNetCoreServer.APIGatewayProxyFunction, which extracts those claims and adds them to Request.HttpContext.User.Claims.
Kestrel of course doesn't support that and AWS ASPNET Cognito Identity Provider seems to be meant for performing the same things that the authorizer is doing.
Solution?
So I got the idea that maybe I can add some custom code to extract it. The HTTP request injected into lambda looks like this, so I expect it should be the same when it's proxied into ECS
{
"resource": "/{proxy+}",
"path": "/api/authtest",
"httpMethod": "GET",
"headers": {
<...>
},
"queryStringParameters": null,
"pathParameters": {
"proxy": "api/authtest"
},
"requestContext": {
"resourceId": "8gffya",
"authorizer": {
"cognito:groups": "Admin",
"phone_number_verified": "true",
"cognito:username": "normj",
"aud": "3mushfc8sgm8uoacvif5vhkt49",
"event_id": "75760f58-f984-11e7-8d4a-2389efc50d68",
"token_use": "id",
"auth_time": "1515973296",
"you_are_special": "true"
}
<...>
}
Is it possible, and how could I go about it to add all the key / value pairs from requestContext.authorizer to Request.HttpContext.User.Claims?
I found a different solution for this.
Instead of trying to modify the HttpContext I map the authorizer output to request headers in the API Gateway integration. Downside of this is that each claim needs to be hardcoded as it doesn't seem to be possible to iterate over them.
Example terraform
resource "aws_api_gateway_integration" "integration" {
rest_api_id = "${var.aws_apigateway-id}"
resource_id = "${aws_api_gateway_resource.proxyresource.id}"
http_method = "${aws_api_gateway_method.method.http_method}"
integration_http_method = "ANY"
type = "HTTP_PROXY"
uri = "http://${aws_lb.nlb.dns_name}/{proxy}"
connection_type = "VPC_LINK"
connection_id = "${aws_api_gateway_vpc_link.is_vpc_link.id}"
request_parameters = {
"integration.request.path.proxy" = "method.request.path.proxy"
"integration.request.header.Authorizer-ResourceId" = "context.authorizer.resourceId"
"integration.request.header.Authorizer-ResourceName" = "context.authorizer.resourceName"
"integration.request.header.Authorizer-Scopes" = "context.authorizer.scopes"
"integration.request.header.Authorizer-TokenType" = "context.authorizer.tokenType"
}
}

How to use a MarkLogic DataHub importFlow from inside another database

I have a usecase that needs to query some data in one database and then use this data as a new input into the MarkLogic DataHub pipeline.
I created a working import and harmonization flow.
Now I want to run the import flow from another database to insert data into th staging database in the dhf.
'use strict';
let id= "/ClueyTest/track/cluey/a2c5c32c-6e99-47c9-8b4d-5b97897509f7.json";
let options = {"dhf.projectName":"ClueyTest", "entity":"Track", "flow":"ImportClueyTracks", "flowType":"input", "dataFormat":"json"};
let rawContent = {
"trackId": "a2c5c32c-6e99-47c9-8b4d-5b97897509f7",
"type": "Feature",
"geometry": {
"type": "LineString",
"coordinates": [
[5.4701967, 51.8190698],
[5.470028, 51.8193624],
[5.470038, 51.8193624],
[5.470048, 51.8193624],
[5.470028, 51.8193634]]
}
,
"properties": {
"timestamps": [
"2019-02-14T16:52:06+0100",
"2019-02-14T16:51:07+0100",
"2019-02-14T16:43:24+0100",
"2019-02-14T16:43:24+0100",
"2019-02-14T16:43:24+0100"
]
}
,
"tracktype": "on",
"endTimestamp": "2019-02-14T16:51:07+0100",
"startTimestamp": "2019-02-14T14:46:50+0100"
}
const clt = require('/entities/clueyTrack/input/ImportClueyTracks/main.sjs');
// the main in the import flow
clt.main(id,rawContent,options);
Obviously you need a working importflow inside your datahub to run this code but the question is about the general usecase how to run an import flow not from gradle but from inside a marklogic database.
All dhf code is sjs.
I think using the Server-Side Library would be most elegant, but I think it does require DataHub v4+:
https://marklogic.github.io/marklogic-data-hub/refs/server-side-library/
HTH!

Accessing a cloud hub API

https://anypoint.mulesoft.com/apiplatform/anypoint-platform/#/portals/organizations/68ef9520-24e9-4cf2-b2f5-620025690913/apis/8617/versions/40329/pages/35412
/applications/{domain}/logs Traits: environment_based
Retrieve log messages for the application, ordered newest to oldest.
I am trying to access this api but am unable to relate what client id does it ask. Also I am unable to relate to oauth authentication this needs.
I am new to mule.
i am sharing the steps by step instructions to access the details of apps from api.
Step 1 : Get the access token from the Api
https://anypoint.mulesoft.com/accounts/login?username=YOUR_USERNAME&password=YOUR_PASSWORD
NOTE : Use POST method and add Header Content-Type=application/json
You will get response in JSON format like below
{
"access_token": "44126898-7ed8-4453-8d28-skajnbf",
"token_type": "bearer",
"redirectUrl": "/home/"
}
Step 2: Get your organization id
https://anypoint.mulesoft.com/accounts/api/me
NOTE : Use GET method and add below Headers
Content-Type = application/json
Authorization = Bearer ACCESS_TOKE_YOU_GOT_ABOVE
Example : Authorization = Bearer 44126898-7ed8-4453-8d28-skajnbf
In the response you will have a section where you will get you organization related details like below
"organization": {
"name": "Sample",
"id": "c1e68d1e-797d-47a5-b",
"createdAt": "2016-11-29T09:45:27.903Z",
"updatedAt": "2016-11-29T09:45:27.932Z",
"ownerId": "68df9a5",
"clientId": "7200350999564690",
"domain": "******",
"idprovider_id": "mulesoft",
"isFederated": false,
"parentOrganizationIds": [],
"subOrganizationIds": [],
"tenantOrganizationIds": [],
"isMaster": true,
"subscription": {
"type": "Trial",
"expiration": "2016-12-29T09:45:27.906Z"
},
Step 3: Get the environment Details
https://anypoint.mulesoft.com/accounts/api/organizations/YOUR_ORGANIZATION_ID_FROM_ABOVE/environments
NOTE : Use GET method and add below Headers
Content-Type = application/json
Authorization = Bearer ACCESS_TOKE_YOU_GOT_ABOVE
Example : https://anypoint.mulesoft.com/accounts/api/organizations/c1e68d1e-797d-47a5-b/environments
You will get all available environments in the response in JSON format as below
{
"data": [
{
"id": "042c933d-82ec-453c-99b2-asmbd",
"name": "Production",
"organizationId": "c1e68d1e-797d-47a5-b726-77asd",
"isProduction": true
}
],
"total": 1
}
Step 4: Now specify the domain name and fetch the logs
https://anypoint.mulesoft.com/cloudhub/api/v2/applications/YOUR_CLOUDHUB_APP_NAME/logs
Example : https://anypoint.mulesoft.com/cloudhub/api/v2/applications/first-test-api-application/logs
NOTE : Use GET method and add below Headers
Content-Type = application/json
Authorization = Bearer ACCESS_TOKE_YOU_GOT_ABOVE
X-ANYPNT-ENV-ID = ENVIRONMENT_ID_YOU_GOT_ABOVE
Example : X-ANYPNT-ENV-ID = 042c933d-82ec-453c-99b2-asmbd
You will get the logs in JSON format as below
{
"data": [
{
"loggerName": "Platform",
"threadName": "system",
"timestamp": 1480503796819,
"message": "Deploying application to 1 workers.",
"priority": "SYSTEM",
"instanceId": "583eb1f1c4b27"
},
{
"loggerName": "Platform",
"threadName": "system",
"timestamp": 1480503797404,
"message": "Provisioning CloudHub worker...",
"priority": "SYSTEM",
"instanceId": "583eb1f1e4b27"
}
],
"total": 2
}
NOTE : FOR ENHANCED LOGGING YOU SHOULD SELECT APPROPRIATE DEPLOYMENT AND INSTANCE IDs TO GET LOGS IN SIMILAR MANNER
Hope this Helps for Beginners
To see the clientID. Log into your CloudHub account. Click on the "gear" icon in the upper right corner. Click on the name of your organisation. you should now see your "clientID" and the "ClientSecret" ID.
Before you use the CloudHub APIs or the Anypoint platform APIs you have to create an account on the Anypoint Platform - Check the architecture of the Anypoint API platform #
https://docs.mulesoft.com/anypoint-platform-for-apis/anypoint-platform-for-apis-system-architecture
Once your are done with the registration with the Anypoint API platform you need to set up users, roles & privileges as an admin -
https://docs.mulesoft.com/anypoint-platform-administration/managing-accounts-roles-and-permissions
As admin you need to control access to APIs by creating & supplying client Id and client Secret - https://docs.mulesoft.com/anypoint-platform-administration/manage-your-organization-and-business-groups
I guess that's the client you referring to. It needs to be present in the request for all the APIs.
As far as OAuth is concerned, it is not completely functional on Cloudhub API. You will have to raise a ticket for support. Check this out -
https://docs.mulesoft.com/mule-user-guide/v/3.7/mule-secure-token-service
If you are new to Mule, run through the Mule Intro videos and try out the Anypoint Studio to get feel of Mulesoft Applications.
Hope this helps.