Unable to regenerate storage key with Azure Management API - azure

I can't use /regenerateKey [1] to regenerate keys for a Storage Account with the Azure Management API.
I'm using the following code in JavaScript (the resource has the subscription removed)
const { ClientSecretCredential } = require('#azure/identity');
const { SecretClient } = require('#azure/keyvault-secrets');
const MSRestAzure = require('ms-rest-azure');
const keyVaultName = process.env.KEY_VAULT_NAME;
const KVUri = `https://${keyVaultName}.vault.azure.net`;
const credential = new ClientSecretCredential(
process.env.AZURE_TENANT_ID,
process.env.AZURE_CLIENT_ID,
process.env.AZURE_CLIENT_SECRET,
);
const vault = new SecretClient(KVUri, credential);
function getCreds() {
return new Promise((res, rej) => {
MSRestAzure.loginWithServicePrincipalSecret(
process.env.AZURE_CLIENT_ID,
process.env.AZURE_CLIENT_SECRET,
process.env.AZURE_TENANT_ID,
(err, creds) => {
if (err) {
rej(err);
return;
}
res(creds);
},
);
});
}
const getResourceUrl = (resource, action) => `https://management.azure.com${resource}/${action}?api-version=2019-04-01`;
const resource = "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myRg/providers/Microsoft.Storage/storageAccounts/MyStore
const creds = await getCreds();
const client = new MSRestAzure.AzureServiceClient(creds);
const regenUrl = getResourceUrl(resource, 'regenerateKey');
await client.sendRequest({ method: 'POST', url: regenUrl }).then(console.log);
I'm getting an UnexpectedException response -
{
"error": {
"code": "UnexpectedException",
"message": "The server was unable to complete your request."
}
}
The Client ID/Secret belongs to an app registration that has access to the storage account, as well as Contributor and Storage Account Key Operator over that subscription.
I'm lead to think that I've not formed the request properly.

I am able to reproduce the error if I don't specify the request body.
Please provide the request body in the following format:
{
keyName: "key1 or key2 (basically which key you want to regenerate)"
}

Related

How to use service account to authenticate google workspace admin api?

I obtained a service account JSON file and also attached domain wide delegation permissions to that service account. Next I set the service account file path using the GOOGLE_APPLICATION_CREDENTIALS env variable. After that I tried to access google groups of the domain like this:
import { google } from 'googleapis';
const admin = await google.admin({
version: 'directory_v1',
});
const groupsResponse = await admin.groups.list({
domain: process.env.GOOGLE_DOMAIN,
});
This gives me the following error:
Request is missing required authentication credential. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.
What am I missing here?
You need to apply the client to the service object.
auth: client
you may want to check out using-the-keyfile-property
Try this
const google = require("googleapis").google;
const SRVC_ACCOUNT_CREDS = require('./keys.json');
const getClient = async (scopes: string[], user: string)=>{
const auth = new google.auth.GoogleAuth({
credentials: SRVC_ACCOUNT_CREDS,
scopes: scopes
});
const client = await auth.getClient();
client.subject = user;
return client;
};
const listUsers = async (query = "", limit = 500, pageToken = null, user, fields, getAll = false)=>{
const scopes = ["https://www.googleapis.com/auth/admin.directory.user"];
const client = await getClient(scopes, user);
const service = google.admin({version: "directory_v1", auth: client});
const result = {
users: [],
nextPageToken: ""
};
if(!fields) {
fields = "users(name.fullName,primaryEmail,organizations(department,primary,title),thumbnailPhotoUrl),nextPageToken";
}
do{
const request = await service.users.list({
customer: "my_customer",
fields: fields,
orderBy: "givenName",
maxResults: limit,
pageToken: pageToken,
query: query,
viewType: "admin_view"
});
pageToken = getAll ? request.data.nextPageToken : null;
const users = request.data.users;
if(users && users.length){
result.users.push(...users);
result.nextPageToken = request.data.nextPageToken;
}
} while(pageToken);
return result;
};

Setting IAM Policy on a function using projects.locations.functions.setIamPolicy Gives error

I'm trying to set IAM policy using projects.locations.functions.setIamPolicy, on a function which I created and deployed, but I keep receiving the following error
GaxiosError: Permission 'cloudfunctions.functions.setIamPolicy' denied on resource '
Here is my code in Node.js
const { auth } = require('google-auth-library');
const { google } = require('googleapis');
const fs = require('fs');
const path = require('path');
function getCredentials() {
const filePath = path.join(__dirname, 'mykeyfile.json');
console.log(filePath);
if (fs.existsSync(filePath)) {
let rawdata = fs.readFileSync(filePath);
let jsonData = JSON.parse(rawdata);
return jsonData;
}
return null;
}
async function setPolicy() {
try {
const credentials = getCredentials();
if (!credentials)
return;
const client = auth.fromJSON(credentials);
client.scopes = ['https://www.googleapis.com/auth/cloud-platform'];
const cloudfunctions = await google.cloudfunctions({
version: 'v1',
auth: client
});
const request = {
// REQUIRED: The resource for which the policy is being specified.
// See the operation documentation for the appropriate value for this field.
resource_: "projects/{myprorjectid}/locations/{zone}/functions/function-1",
resource: {
"policy": {
"etag": "BwWP3fXnMuQ=",
"version": 1,
"bindings": [
{
"members": [
"allUsers"
],
"role": "roles/cloudfunctions.invoker"
}
]
}
},
auth: client,
};
const response = (await cloudfunctions.projects.locations.functions.setIamPolicy(request)).data;
console.log(JSON.stringify(response, null, 2));
} catch (err) {
console.log(err);
}
}
setIamPolicy();
Any help is much appreciated. Not interested in command like options. Must be done in Node.
It seems that the service account you're using on your code doesn't have the right permissions. The error is similar to this doc and the solution is to add Project Owner or Cloud Functions Admin role to your service account, as both contain the cloudfunctions.functions.setIamPolicy permission.

Why getting this "Authentication_MissingOrMalformed" error during Microsoft Graph API call?

const msRestAzure = require('ms-rest-azure');
const { GraphRbacManagementClient } = require('azure-graph');
module.exports = async function (context, req) {
try{
const credentials = await msRestAzure.loginWithServicePrincipalSecret(clientId, clientSecret, tanent);
const client = new GraphRbacManagementClient(credentials, tenantId);
const results = await client.users.list();
context.res = {
body: results
};
} catch (error) {
console.log('error==> ',error); // Getting error: Authentication_MissingOrMalformed
context.res = {
body: error
};
}
}
I want to get all users list using azure graph sdk. But after calling the client.users.list() function I'm getting the error ("Authentication_MissingOrMalformed"). How do I fix this error and get all users list.
How to get all users list from Azure Active Directory using Azure Graph SDK (Nodejs) ?
The main problem is missing { tokenAudience: 'graph' }, please refer to my code:
const msRestAzure = require('ms-rest-azure');
const { GraphRbacManagementClient } = require('azure-graph');
module.exports = async function (context, req) {
try{
msRestAzure.loginWithServicePrincipalSecret("clientId", "clientSecret", "tenantId", { tokenAudience: 'graph' }, function (err, credentials) {
if (err) return console.log(err);
const client = new GraphRbacManagementClient(credentials, "tenantId");
client.users.list((err, results, request, response) => {
if (err) return console.log(err);
console.log(JSON.parse(response.body).value.length);
});
});
} catch (error) {
console.log('error==> ',error);
context.res = {
body: error
};
}
}
After running the code above, if the number of users in your AD is greater than 100, it will output 100 because graph api can response 100 users in a page(default is 100).
==================================Update================================
Please check if you have added the permission to the application registered in Azure AD. If you didn't add the permission, please follow the below steps:
1. Go to the application which registered in your Azure AD (It's the application which you use its clientId).
2. Add the permission.
3. Click "Grant admin consent for xxx".
4. After a few minutes, run your code again.

Access azure billing API

I would like to create a dashboard with graphs about costs of my azure resources (as detailed as possible). Meaning, a list of monthly invoices is not enough (but I would already be very happy if I a could achieve that!!)
Anyway, the first thing I noticed is that if you find an example the endpoint urls look like this
https://management.azure.com/subscriptions/${subscriptionId}/resourcegroups?api-version=2016-09-01
Check the end of the url 2016-09-01, doesn't look very up2date. This medium post was the best article I could find, but it also uses these urls.
Furthermore, I was not able to follow the steps described, first it uses postman to retrieve an access_token (not very useful for me because I need it automated) and second, somewhere in the middle an access_token is retrieved but never used.
So, I found a npm packages like [azure-arm-billing][2] from which I was able to write the following program (mostly copy-paste):
const msRestAzure = require('ms-rest-azure');
const BillingManagement = require('azure-arm-billing')
const clientId = process.env['CLIENT_ID'];
const secret = process.env['APPLICATION_SECRET'];
const domain = process.env['DOMAIN'];
const subscriptionId = process.env['AZURE_SUBSCRIPTION_ID'];
// Retrieve access_token
const app = new msRestAzure.ApplicationTokenCredentials(clientId, domain, secret);
app.getToken((err, token) => {
console.log(token.accessToken);
});
// =======
msRestAzure
.interactiveLogin( { domain }) // The argument here is nowhere documented
.then(credentials => {
console.log(credentials);
let client = new BillingManagement(credentials, subscriptionId);
return client.invoices.list();
})
.then(invoices => {
console.log('List of invoices:');
console.dir(invoices, { depth: null, colors: true });
});
Running this shows a nice access_token and invoices
...
List of invoices:
[
{
id: '/subscriptions/../providers/Microsoft.Billing/invoices/....',
name: '...',
type: 'Microsoft.Billing/invoices',
invoicePeriodStartDate: 2019-08-25T00:00:00.000Z,
invoicePeriodEndDate: 2019-09-24T00:00:00.000Z,
billingPeriodIds: [
'/subscriptions/.../pr..s/Micro..ing/bill..ods/201910-1'
]
},
{
id: '/subscriptions/9ea...3d/providers/Microsoft.Billing/invoices/201909-...',
name: '....',
type: 'Microsoft.Billing/invoices',
invoicePeriodStartDate: 2019-07-25T00:00:00.000Z,
invoicePeriodEndDate: 2019-08-24T00:00:00.000Z,
billingPeriodIds: [
'/subscriptions/..../providers/Microsoft.Billing/billingPeriods/201909-1...'
]
}
]
Although I have my invoices, there are no numbers. And I would like to retrieve costs for every resources.
So the documentation seems to be outdated up to not existing for what I want (as it seems). My question is if someone was able to retrieve information like this? I would really like to know how!!
UPDATE
It seems to be a permission issue. So, below I share some screenshots showing what I have right now. Maybe from these it is clear what I miss or have setup incorrectly. So first, here is my latest nodejs app:
const msRestAzure = require("ms-rest-azure");
const ConsumptionManagementClient = require("azure-arm-consumption");
const clientId = '76d79....'; // App registration ID
const secret = '****...'; // App registration secret
const domain = 'dc36...'; // tenantId
const subscriptionId = '9ea2d...'; // subscription ID
const AzureServiceClient = msRestAzure.AzureServiceClient;
//an example to list resource groups in a subscription
msRestAzure.loginWithServicePrincipalSecret(clientId, secret, domain).then((creds) => {
const client = new ConsumptionManagementClient(creds, subscriptionId);
const expand = '';
const filter = '';
const skiptoken = '';
const top = 1000;
const apply = '';
return client.usageDetails.list(expand, filter, skiptoken, top, apply).then(result => {
console.log('The result is:', result);
});
}).catch((err) => {
console.log('An error occurred:');
console.dir(err, { depth: null, colors: true });
});
Which outputs a statusCode 401
Error: Unauthorized. Request ID: e6b127...
...
So, I have in AD an App registration
Its API permissions are
Finally, I have just one subscription
With the following IAM settings
Any suspicious?
If you're looking for resource costs, I would suggest that you take a look at Consumption API - List Usage Details. That will give you the consumption for all the resources.
You will need to install azure-arm-consumption package.
Here's the sample code:
const msRestAzure = require("ms-rest-azure");
const ConsumptionManagementClient = require("azure-arm-consumption");
msRestAzure.interactiveLogin().then((creds) => {
const subscriptionId = "<your subscription id>";
const client = new ConsumptionManagementClient(creds, subscriptionId);
const expand = "";
const filter = "";
const skiptoken = "";
const top = 1000;
const apply = "";
return client.usageDetails.list(expand, filter, skiptoken, top, apply).then((result) => {
console.log("The result is:");
console.log(result);
});
}).catch((err) => {
console.log('An error occurred:');
console.dir(err, {depth: null, colors: true});
});
This is taken from here: https://github.com/Azure/azure-sdk-for-node/tree/master/lib/services/consumptionManagement.

Grant access to Common Data Service with adal-node

Cannot grant access to Common Data Service with NodeJS
I am implementing a simple Node function which will get some data from Common Data Service. I can get the accessToken already, but when I use this accessToken to access Common Data Service, the response is ‘Unauthorized’.
I followed the instruction here ( https://learn.microsoft.com/en-us/powerapps/developer/common-data-service/walkthrough-registering-configuring-simplespa-application-adal-js ) and is able to get it worked with simple page app.
I just want to port it to Node and have the app grant access to Common Data Service without requiring a user to login.
const fetch = require('node-fetch');
const AuthenticationContext = require('adal-node').AuthenticationContext;
module.exports = async function (context, req) {
context.log('JavaScript HTTP trigger function processed a request.');
const resource = "https://my-org.crm5.dynamics.com";
const clientId = 'my client id';
const clientSecret = 'my client secret';
const authorityHostUrl = 'https://login.microsoftonline.com';
const tenant = 'my-tenant-name.onmicrosoft.com'; // AAD Tenant name.
const authorityUrl = authorityHostUrl + '/' + tenant;
const authContext = new AuthenticationContext(authorityUrl);
const tokenResp = await new Promise((resolve, reject) => {
authContext.acquireTokenWithClientCredentials(resource, clientId, clientSecret, function (err, tokenResponse) {
if (err) {
context.error("cannot get token: " + err.stack);
return reject(err.stack);
} else {
return resolve(tokenResponse);
}
});
});
context.log("tokenResp: ", tokenResp); // The tokenResp contains accessToken
const cdsHeaders = {};
cdsHeaders["Authorization"] = "Bearer " + tokenResp.accessToken;
cdsHeaders["Accept"] = "application/json";
cdsHeaders["Content-Type"] = "application/json; charset=utf-8";
cdsHeaders["OData-MaxVersion"] = "4.0";
cdsHeaders["OData-Version"] = "4.0";
const endpointUrl = encodeURI(resource + "/api/data/v9.0/accounts?$select=name,address1_city&$top=10");
const dataResponse = await fetch(endpointUrl, { method: 'GET', headers: cdsHeaders });
console.log("response: ", dataResponse); // The dataResponse is 401 Unauthorized
context.res = { body: "Done" };
};
I got the solution: I have to 'Manually create a CDS for Apps application user' in order for it to work, regarding this document: https://learn.microsoft.com/en-us/powerapps/developer/common-data-service/authenticate-oauth#connect-as-an-app
Although the sample code is in C#, there are not too many differences between C# and Node.js clients.

Resources