How to Query Azure Table storage using .Net Standard - c#

I have a .Net Standard client application running on UWP.
My client application contacts the server that generates a sas key like so:
var myPrivateStorageAccount = CloudStorageAccount.Parse(mystorageAccountKey);
var myPrivateTableClient = myPrivateStorageAccount.CreateCloudTableClient();
SharedAccessTablePolicy pol = new SharedAccessTablePolicy()
{
SharedAccessExpiryTime = DateTime.UtcNow.AddMinutes(59),
Permissions = SharedAccessTablePermissions.Query | SharedAccessTablePermissions.Add
};
CloudTable myPrivateTable = myPrivateTableClient.GetTableReference(tableName);
String sas = myPrivateTable.GetSharedAccessSignature(pol);
return sas;
My client application then runs the following:
StorageCredentials creds = new StorageCredentials(sas);
this.tableClient = new CloudTableClient(tableServiceURI, creds);
this.table = tableClient.GetTableReference(tableName);
TableQuery<DynamicTableEntity> projectionQuery = new TableQuery<DynamicTableEntity>().Select(new string[] { "DocumentName" }).Where(TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, cc));
var res = await table.ExecuteQuerySegmentedAsync<DynamicTableEntity>(projectionQuery, null);
and gets the following error:
Server failed to authenticate the request. Make sure the value of
Authorization header is formed correctly including the signature. sr
is mandatory. Cannot be empty
but as this is tablestorage I dont think sr is required
and my SAS key looks fine to me:
?sv=2018-03-28&tn=MyTable&sig=RandomSig151235341543&st=2019-01-17T12%3A00%3A28Z&se=2019-01-17T12%3A59%3A28Z&sp=ra
so whats the problem here?

Ok so this is kind of stupid but I will post anyway.
I was sending the request to:
https://myaccount.blob.core.windows.net/
and should have been sending the request to:
https://myaccount.table.core.windows.net/

Related

Create a Cosmos Db and Container in C#

I am trying to create a database and container using C# I am trying to use the newest version of Azure.ResourceManager.CosmosDB 1.2.0.
In a previos version I used:
var client = new CosmosDBManagementClient("someendpoint", new DefaultAzureCredential());
var database = await client.SqlResources.StartCreateUpdateSqlDatabaseAsync(
AzureResourceGroup, AccountName, DatabaseName,
new SqlDatabaseCreateUpdateParameters(
new SqlDatabaseResource(DatabaseName),
new CreateUpdateOptions()
)
);
var armcontainer = await client.SqlResources.StartCreateUpdateSqlContainerAsync(
AzureResourceGroup, AccountName, DatabaseName,
ContainerName,
GetContainerParameters()
);
However, the CosmosDBManagementClient is no longer in library.
I know there is:
var client = new CosmosClient(endpoint, new DefaultAzureCredential());
await client.CreateDatabaseIfNotExistsAsync("testing",
throughput: null, new RequestOptions());
I also can't get this to work due to 403 error, even with the contributor permissions, but I know this was an error because you are supposed to use the resource manager.
Any suggestions would be appreciated.
I haven't worked with passwordless access but have you tried providing the id explictely?
string userAssignedClientId = "<your managed identity client Id>";
var credential = new DefaultAzureCredential(new DefaultAzureCredentialOptions { ManagedIdentityClientId = userAssignedClientId });
var client = new CosmosClient(endpoint, new DefaultAzureCredential());
If this doesn't work you could try the connection string approach:
using CosmosClient client = new(
accountEndpoint: "endpoint here",
authKeyOrResourceToken: "key here");

Get user data from a TokenCredential object (.NET)

I'm building a .NET core tool (Console app) that needs to access some Azure Keyvault secrets by using a SecretClient. This client needs a TokenCredential for which I use DefaultAzureCredential().
The client is successfully authenticated and retrieves the secrets, but can I know which method from the flow was used (i.e. Environment, Cache, CLI, interactive)? I want to display the username that was used for logged in, since you might have an account in SharedCache but you might want to use another account.
var credentials = new DefaultAzureCredential();
var secretClient = new SecretClient(new Uri(configuration["Authentication:KeyVaultUri"]), credentials);
// Just using the client to retrieve values
var settings = JsonSerializer.Deserialize<AppSettingsKeys>((await secretClient.GetSecretAsync(configuration["Authentication:SecretName"])).Value.Value);
I checked the credential object but didn't see anything useful to get the username. I want to Console.WriteLine something like Successfully logged in with pepe#test.com using SharedTokenCacheCredential
I was able to get the upn by first getting the jwt with the GetToken method, and then parsing it with a JwtSecurityTokenHandler.
Not the approach I was looking for but it works, I was wondering if there is cleaner way.
var credential = new DefaultAzureCredential();
var secretClient = new SecretClient(new Uri(configuration["Authentication:KeyVaultUri"]), credential);
var settings = JsonSerializer.Deserialize<AppSettingsKeys>((await secretClient.GetSecretAsync(configuration["Authentication:SecretName"])).Value.Value);
var token = await credential.GetTokenAsync(
new Azure.Core.TokenRequestContext(
new[] { "https://vault.azure.net/.default" }));
var handler = new JwtSecurityTokenHandler();
var jsonToken = handler.ReadToken(token.Token) as JwtSecurityToken;
var upn = jsonToken.Claims.First(c => c.Type=="upn").Value;

How to export azure database to blob storage

I need to know exactly how to login to Azure, using c#.
I basically want to do this, but from the code:
]a link](https://learn.microsoft.com/en-us/azure/sql-database/sql-database-export)
Here is the code I copied from the internet trying to achieve this:
But I don't know how to generate the token.
SqlManagementClient managementClient = new SqlManagementClient(new TokenCloudCredentials(subscriptionId, GetAccessToken(tenantId, clientId, secretKey)));
var exportParams = new DacExportParameters()
{
BlobCredentials = new DacExportParameters.BlobCredentialsParameter()
{
StorageAccessKey = storageKey,
Uri = new Uri(baseStorageUri)
},
ConnectionInfo = new DacExportParameters.ConnectionInfoParameter()
{
ServerName = azureSqlServer,
DatabaseName = azureSqlDatabase,
UserName = adminLogin,
Password = adminPassword
}
};
var exportResult = managementClient.Dac.Export(azureSqlServerName, exportParams);
I have a GetToken function, but I have no idea where to take the
tenant + client id + secret
private static string GetAccessToken(string tenantId, string
clientId, string secretKey)
{
var authenticationContext = new
AuthenticationContext($"https://login.windows.net/{tenantId}");
var credential = new ClientCredential(clientId, secretKey);
var result =authenticationContext
.AcquireTokenAsync("https://management.core.windows.net/",
credential);
if (result == null)
{
throw new InvalidOperationException("Failed to obtain the JWT token");
}
var token = result.Result.AccessToken;
return token;
}
This question was asked before
Azure Database export with C#
but I need to see the actual code and explanation on how to get the connection info.
I need to see the actual code and explanation on how to get the connection info.
I would recommend you follow this tutorial about registering your AAD application and adding the secret key. Moreover, you could also follow Using the Azure ARM REST API – Get Access Token.
SqlManagementClient managementClient = new SqlManagementClient(new TokenCloudCredentials(subscriptionId, GetAccessToken(tenantId, clientId, secretKey)));
Based on your code, I assumed that you are using the package Microsoft.WindowsAzure.Management.Sql, if you use the TokenCloudCredentials, you may receive the following error response:
AFAIK, Microsoft.WindowsAzure.Management.Libraries requires the X509Certificate2 authentication, you need to construct the CertificateCloudCredentials for your SqlManagementClient. For uploading a management certificate under your subscription, you could follow Upload an Azure Service Management Certificate. For retrieving the X509Certificate2 instance, you could follow the code snippet under the Authenticate using a management certificate section from here.
For token-based authentication, you could use the package Microsoft.Azure.Management.Sql and construct your SqlManagementClient as follows:
var sqlManagement = new SqlManagementClient(new TokenCredentials("{access-token}"));
Moreover, you need to change the resource from https://management.core.windows.net/ to https://management.azure.com/ when invoking the AcquireTokenAsync method.

Error when calling any method on Service Management API

I'm looking to start an Azure runbook from a c# application which will be hosted on an Azure web app.
I'm using certificate authentication (in an attempt just to test that I can connect and retrieve some data)
Here's my code so far:
var cert = ConfigurationManager.AppSettings["mgmtCertificate"];
var creds = new Microsoft.Azure.CertificateCloudCredentials("<my-sub-id>",
new X509Certificate2(Convert.FromBase64String(cert)));
var client = new Microsoft.Azure.Management.Automation.AutomationManagementClient(creds, new Uri("https://management.core.windows.net/"));
var content = client.Runbooks.List("<resource-group-id>", "<automation-account-name>");
Every time I run this, no matter what certificate I use I get the same error:
An unhandled exception of type 'Hyak.Common.CloudException' occurred in Microsoft.Threading.Tasks.dll
Additional information: ForbiddenError: The server failed to authenticate the request. Verify that the certificate is valid and is associated with this subscription.
I've tried downloading the settings file which contains the automatically generated management certificate you get when you spin up the Azure account... nothing I do will let me talk to any of the Azure subscription
Am I missing something fundamental here?
Edit: some additional info...
So I decided to create an application and use the JWT authentication method.
I've added an application, given the application permissions to the Azure Service Management API and ensured the user is a co-administrator and I still get the same error, even with the token...
const string tenantId = "xx";
const string clientId = "xx";
var context = new AuthenticationContext(string.Format("https://login.windows.net/{0}", tenantId));
var user = "<user>";
var pwd = "<pass>";
var userCred = new UserCredential(user, pwd);
var result = context.AcquireToken("https://management.core.windows.net/", clientId, userCred);
var token = result.CreateAuthorizationHeader().Substring("Bearer ".Length); // Token comes back fine and I can inspect and see that it's valid for 1 hour - all looks ok...
var sub = "<subscription-id>";
var creds = new TokenCloudCredentials(sub, token);
var client = new AutomationManagementClient(creds, new Uri("https://management.core.windows.net/"));
var content = client.Runbooks.List("<resource-group>", "<automation-id>");
I've also tried using other Azure libs (like auth, datacentre etc) and I get the same error:
ForbiddenError: The server failed to authenticate the request. Verify that the certificate is valid and is associated with this subscription.
I'm sure it's just 1 tickbox I need to tick buried somewhere in that monolithic Management Portal but I've followed a few tutorials on how to do this and they all end up with this error...
public async Task StartAzureRunbook()
{
try
{
var subscriptionId = "azure subscription Id";
string base64cer = "****long string here****"; //taken from http://stackoverflow.com/questions/24999518/azure-api-the-server-failed-to-authenticate-the-request
var cert = new X509Certificate2(Convert.FromBase64String(base64cer));
var client = new Microsoft.Azure.Management.Automation.AutomationManagementClient(new CertificateCloudCredentials(subscriptionId, cert));
var ct = new CancellationToken();
var content = await client.Runbooks.ListByNameAsync("MyAutomationAccountName", "MyRunbookName", ct);
var firstOrDefault = content?.Runbooks.FirstOrDefault();
if (firstOrDefault != null)
{
var operation = client.Runbooks.Start("MyAutomationAccountName", new RunbookStartParameters(firstOrDefault.Id));
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
Also in portal:
1) Application is multitenant
2) Permissions to other applications section - Windows Azure Service Manager - Delegated permissions "Access Azure Service Management(preview)"
Ensure that your Management certificate has private key and was not made from the .CER file. The fact that you're not supplying a password when generating the X509Certificate object makes me think you're using public key only
Ensure that your Managemnet's certificate public key (.CER file) has been uploaded to the Azure management portal (legacy version, Management Certificate area)
Use CertificateCloudCredentials and not any other credential type of an object
Ok, stupid really but one of the tutorials I followed suggested installing the prerelease version of the libs.
Installing the preview (0.15.2-preview) has fixed the issue!

Google Analytics Api on Azure

I want to use the google analytics api in my MVC website, im authenticating using the api service account and oauth2 with have no issues on my localhost but as soon as I deploy to Azure i get a 502 error:
"502 - Web server received an invalid response while acting as a
gateway or proxy server. There is a problem with the page you are
looking for, and it cannot be displayed. When the Web server (while
acting as a gateway or proxy) contacted the upstream content server,
it received an invalid response from the content server."
heres my code:
const string ServiceAccountUser = "xxxxxxxxxx-cpla4j8focrebami0l87mbcto09j9j6k#developer.gserviceaccount.com";
AssertionFlowClient client = new AssertionFlowClient(
GoogleAuthenticationServer.Description,
new X509Certificate2(System.Web.Hosting.HostingEnvironment.MapPath("/Areas/Admin/xxxxxxxxxxxxxxxxxx-privatekey.p12"),
"notasecret", X509KeyStorageFlags.Exportable))
{
Scope = AnalyticsService.Scopes.AnalyticsReadonly.GetStringValue(),
ServiceAccountId = ServiceAccountUser //Bug, why does ServiceAccountUser have to be assigned to ServiceAccountId
//,ServiceAccountUser = ServiceAccountUser
};
OAuth2Authenticator<AssertionFlowClient> authenticator = new OAuth2Authenticator<AssertionFlowClient>(client, AssertionFlowClient.GetState);
I cant figure out whats causing it? Am im missing something within Azure?
Thanks for any help.
I also ran into the same issue but passing X509KeyStorageFlags.MachineKeySet into the constructor as well fixed the issue for me.
X509Certificate2 certificate = new X509Certificate2(file, "key", X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet);
After hours of pain on this exact same problem, I found a work around by piecing together various sources of info.
The problem arises from trying to read the p12 file from the Azure web site, i.e. this line in my code fails
var key = new X509Certificate2(keyFile, keyPassword, X509KeyStorageFlags.Exportable);
No idea why, but it works if you split the file into a cer and key.xml file?
Firstly, extract these files, (I just used a console app)
// load pfx/p12 as "exportable"
var p12Cert = new X509Certificate2(#"c:\Temp\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-privatekey.p12", "notasecret", X509KeyStorageFlags.Exportable);
// export .cer from .pfx/.p12
File.WriteAllBytes(#"C:\Temp\MyCert.cer", p12Cert.Export(X509ContentType.Cert));
// export private key XML
string privateKeyXml = p12Cert.PrivateKey.ToXmlString(true);
File.WriteAllText(#"C:\Temp\PrivateKey.xml", privateKeyXml);
Then copy them to your website then load them in like so
//Store the authentication description
AuthorizationServerDescription desc = GoogleAuthenticationServer.Description;
//Create a certificate object to use when authenticating
var rsaCryptoServiceProvider = new RSACryptoServiceProvider();
rsaCryptoServiceProvider.FromXmlString(File.ReadAllText(keyFile));
var key = new X509Certificate2(certFile) {PrivateKey = rsaCryptoServiceProvider};
//Now, we will log in and authenticate, passing in the description
//and key from above, then setting the accountId and scope
var client = new AssertionFlowClient(desc, key)
{
//cliendId is your SERVICE ACCOUNT Email Address from Google APIs Console
//looks something like 12345-randomstring#developer.gserviceaccount.com
//~IMPORTANT~: this email address has to be added to your Google Analytics profile
// and given Read & Analyze permissions
ServiceAccountId = clientId,
Scope = "https://www.googleapis.com/auth/analytics.readonly"
};
//Finally, complete the authentication process
//NOTE: This is the first change from the update above
var auth = new OAuth2Authenticator<AssertionFlowClient>(client, AssertionFlowClient.GetState);
//First, create a new service object
//NOTE: this is the second change from the update
//above. Thanks to James for pointing this out
var gas = new AnalyticsService(new BaseClientService.Initializer { Authenticator = auth });
This now works for me and I hope it helps you.

Categories

Resources