How to connect to Cloud Firestore DB with .net core? - c#

So far all the examples of using Google Cloud Firestore with .net show that you connect to your Firestore db by using this command:
FirestoreDb db = FirestoreDb.Create(projectId);
But is this skipping the step of authentication? I can't seem to find an example of wiring it up to use a Google service account. I'm guessing you need to connect using the service account's private_key/private_key_id/client_email?

You can also use the credentials stored in a json file:
GoogleCredential cred = GoogleCredential.FromFile("credentials.json");
Channel channel = new Channel(FirestoreClient.DefaultEndpoint.Host,
FirestoreClient.DefaultEndpoint.Port,
cred.ToChannelCredentials());
FirestoreClient client = FirestoreClient.Create(channel);
FirestoreDb db = FirestoreDb.Create("my-project", client);

I could not compile #Michael Bleterman's code, however the following worked for me:
using Google.Cloud.Firestore;
using Google.Cloud.Firestore.V1;
var jsonString = File.ReadAllText(_keyFilepath);
var builder = new FirestoreClientBuilder {JsonCredentials = jsonString};
FirestoreDb db = FirestoreDb.Create(_projectId, builder.Build());
Packages I use:
<PackageReference Include="Google.Cloud.Firestore" Version="2.0.0-beta02" />
<PackageReference Include="Google.Cloud.Storage.V1" Version="2.5.0" />

But is this skipping the step of authentication?
No. It will use the default application credentials. If you're running on Google Cloud Platform (AppEngine, GCE or GKE), they will just be the default service account credentials for the instance. Otherwise, you should set the GOOGLE_APPLICATION_CREDENTIALS environment variable to refer to a service account credential file.
From the home page of the user guide you referred to:
When running on Google Cloud Platform, no action needs to be taken to authenticate.
Otherwise, the simplest way of authenticating your API calls is to download a service account JSON file then set the GOOGLE_APPLICATION_CREDENTIALS environment variable to refer to it. The credentials will automatically be used to authenticate. See the Getting Started With Authentication guide for more details.
It's somewhat more awkward to use non-default credentials; this recent issue gives an example.

This worked for me.
https://pieterdlinde.medium.com/netcore-and-cloud-firestore-94628943eb3c
string filepath = "/Users/user/Downloads/user-a4166-firebase-adminsdk-ivk8q-d072fdf334.json";
Environment.SetEnvironmentVariable("GOOGLE_APPLICATION_CREDENTIALS", filepath);
fireStoreDb = FirestoreDb.Create("user-a4166");

The simplest way:
Get service account json file and hardcode values into a class:
public class FirebaseSettings
{
[JsonPropertyName("project_id")]
public string ProjectId => "that-rug-really-tied-the-room-together-72daa";
[JsonPropertyName("private_key_id")]
public string PrivateKeyId => "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
// ... and so on
}
Add it Startup.cs
var firebaseJson = JsonSerializer.Serialize(new FirebaseSettings());
services.AddSingleton(_ => new FirestoreProvider(
new FirestoreDbBuilder
{
ProjectId = firebaseSettings.ProjectId,
JsonCredentials = firebaseJson // <-- service account json file
}.Build()
));
Add wrapper FirebaseProvider
public class FirestoreProvider
{
private readonly FirestoreDb _fireStoreDb = null!;
public FirestoreProvider(FirestoreDb fireStoreDb)
{
_fireStoreDb = fireStoreDb;
}
// ... your methods here
}
Here is a full example of a generic provider.
https://dev.to/kedzior_io/simple-net-core-and-cloud-firestore-setup-1pf9

Related

C# ASP.NET MVC Use of Azure Key Vault Runtime Initialized Variables

I am looking for the most appropriate way to store and/or use variables initialized during startup (Program.cs) throughout the application as needed, or an acceptable alternative process if there's a better way to accomplish this.
I.e., the following code snippet in Program.cs initializes the Azure Key Vault connectionString variable at runtime with a correct value retrieved from the designated Azure Key Vault:
var keyVaultUrl = builder.Configuration.GetValue<string>("KeyVault:KeyVaultUrl");
if (app.Environment.IsDevelopment())
{
app.UseMigrationsEndPoint();
SecretClientOptions options = new SecretClientOptions()
{
Retry =
{
Delay= TimeSpan.FromSeconds(2),
MaxDelay = TimeSpan.FromSeconds(16),
MaxRetries = 5,
Mode = RetryMode.Exponential
}
};
var client = new SecretClient(new Uri(keyVaultUrl), new DefaultAzureCredential(), options);
KeyVaultSecret secretConnectionString = client.GetSecret("ConnectionString");
string connectionString = secretConnectionString.Value;
}
The objective is to use this variable or others on-demand without having to call the code another time. Any thoughts are appreciated.
Check the below steps to store the KeyVault Connection string in Azure Appsettings.
I do agree with #Dai, yes instead of getting the ConnectionString/Varaibles from KeyVault , we can store the values in Azure App Service => Configuration => Application Settings.
My appsettings.json
"ConnectionStrings": {
"MyVal": "DummyString"
}
Create a secret in KeyVault and copy the Secret Identifier.
Azure App Settings
Thanks to #Jayant Kulkarni - reference taken from c-sharpcorner.

How to fix issue calling Amazon SP-API, which always returns Unauthorized, even with valid Token and Signature

I went through the guide of for getting setup to call the new SP-API (https://github.com/amzn/selling-partner-api-docs/blob/main/guides/developer-guide/SellingPartnerApiDeveloperGuide.md), and during the process checked off all of the api areas to grant access to (i.e. Orders, Inventory, etc). I am using the C# library provided by Amazon (https://github.com/amzn/selling-partner-api-models/tree/main/clients/sellingpartner-api-aa-csharp). I successfully get an access token and successfully sign the request, but always get the following error:
Access to requested resource is denied. / Unauthorized, with no details.
I am trying to perform a simple get to the /orders/v0/orders endpoint. What am I doing wrong?
Below is my code:
private const string MARKETPLACE_ID = "ATVPDKIKX0DER";
var resource = $"/orders/v0/orders";
var client = new RestClient("https://sellingpartnerapi-na.amazon.com");
IRestRequest restRequest = new RestRequest(resource, Method.GET);
restRequest.AddParameter("MarketPlaceIds", MARKETPLACE_ID, ParameterType.QueryString);
restRequest.AddParameter("CreatedAfter", DateTime.UtcNow.AddDays(-5), ParameterType.QueryString);
var lwaAuthorizationCredentials = new LWAAuthorizationCredentials
{
ClientId = AMAZON_LWA_CLIENT_ID,
ClientSecret = AMAZON_LWA_CLIENT_SECRET,
RefreshToken = AMAZON_LWA_REFRESH_TOKEN,
Endpoint = new Uri("https://api.amazon.com/auth/o2/token")
};
restRequest = new LWAAuthorizationSigner(lwaAuthorizationCredentials).Sign(restRequest);
var awsAuthenticationCredentials = new AWSAuthenticationCredentials
{
AccessKeyId = AMAZON_ACCESS_KEY_ID,
SecretKey = AMAZON_ACCESS_SECRET,
Region = "us-east-1"
};
restRequest = new AWSSigV4Signer(awsAuthenticationCredentials).Sign(restRequest, client.BaseUrl.Host);
var response = client.Execute(restRequest);
If you followed the SP-API guide, then you created a Role (which is the IAM ARN your app is registered with) and a User which has permissions to assume that role to make API calls.
However, one thing the guide is not clear about is that you can't make API calls using that user's credentials directly. You must first call the STS API's AssumeRole method with your User's credentials (AMAZON_ACCESS_KEY_ID/AMAZON_ACCESS_SECRET), and it will return temporary credentials authorized against the Role. You use those temporary credentials when signing requests.
AssumeRole will also return a session token which you must include with your API calls in a header called X-Amz-Security-Token. For a brief description of X-Amz-Security-Token see https://docs.aws.amazon.com/STS/latest/APIReference/CommonParameters.html
You also get this error if your sp app is under review, drove me nuts!
If you using c# take look to
https://github.com/abuzuhri/Amazon-SP-API-CSharp
AmazonConnection amazonConnection = new AmazonConnection(new AmazonCredential()
{
AccessKey = "AKIAXXXXXXXXXXXXXXX",
SecretKey = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
RoleArn = "arn:aws:iam::XXXXXXXXXXXXX:role/XXXXXXXXXXXX",
ClientId = "amzn1.application-XXX-client.XXXXXXXXXXXXXXXXXXXXXXXXXXXX",
ClientSecret = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
RefreshToken= "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
});
var orders= amazonConnection.Orders.ListOrders();
In our situation, we had to explicitly add an IAM policy to the user we defined as making the API call. Please see the link below and confirm that the user you have calling the API has the policy assigned to them:
https://github.com/amzn/selling-partner-api-docs/blob/main/guides/developer-guide/SellingPartnerApiDeveloperGuide.md#step-3-create-an-iam-policy
Somehow we went through the step-by-step setup twice, and adding this explicit policy was missed. Initially I believe it was added 'inline' as instructed, but that does not seem to work.
I dont think is a duplicated question, buy the solution may apply: https://stackoverflow.com/a/66860192/1034622

Getting Azure App Service restarts as metric

Is it possible to get programmatically the number of app service restarts either as a metric or an alert. Specifically the programmatic equivalent of going to an Azure App service -> Diagnose and solve problems -> Web App restarted and get the dates and times of the restarts.
According to this: Is it possible to see the restart history of an Azure App Service? the activity log also contains the data.
Ideally I want to access this from the Microsoft.Azure.Management.Fluent interface i.e. in C# code.
Yes, you can. Just use the code below to check the activity log, the list in the code is what you want.
Follow the steps below.
1.Register an application with Azure AD and create a service principal.
2.Get values for signing in and create a new application secret.
3.Navigate to the web app -> Access control (IAM) -> Add -> add service principal of the AD App as an RBAC role e.g. Contributor, details follow this.
4.Then use the code below.
using Microsoft.Azure.Management.Monitor.Fluent.Models;
using Microsoft.Azure.Management.ResourceManager.Fluent;
using System;
using System.Collections.Generic;
namespace ConsoleApp14
{
class Program
{
static void Main(string[] args)
{
var clientId = "xxxxx";
var clientSecret = "xxxxx";
var tenantId = "xxxxx";
var subscriptionId = "xxxxx";
var credentials = SdkContext.AzureCredentialsFactory
.FromServicePrincipal(clientId,
clientSecret,
tenantId,
AzureEnvironment.AzureGlobalCloud);
var azure = Microsoft.Azure.Management.Fluent.Azure
.Configure()
.Authenticate(credentials)
.WithSubscription(subscriptionId);
var logs = azure.ActivityLogs.DefineQuery()
.StartingFrom(DateTime.Now.AddDays(-7))
.EndsBefore(DateTime.Now)
.WithAllPropertiesInResponse()
.FilterByResource("/subscriptions/xxxxx/resourceGroups/xxxxx/providers/Microsoft.Web/sites/joyweba")
.Execute();
List<IEventData> list = new List<IEventData>();
foreach (var log in logs) {
if ((log.OperationName.LocalizedValue == "Restart Web App") & (log.Status.LocalizedValue == "Succeeded")) {
list.Add(log);
}
}
}
}
}

Azure keyvault from on prem .net app without exposing clientid/clientsecret?

I've registered my app in azure AD and created a client secret and then created a vault and added a secret for the dbconnectionstring below. It works ok but I need the "client-id" and "client-secret" since the identity is managed as service principal. Is there a way to get thos values through an API so that my app doesn't have to save those in the config? It's kind of defeating the purpose since thos whole exercise was to avoid having to save connection strings in the web.config/appsettings.json; as now I can save those in the vault but I would need to save the clientid/secret in the config.
var kvClient = new KeyVaultClient(async (authority, resource, scope) =>
{
var context = new AuthenticationContext(authority);
var credential = new ClientCredential("client-id", "client-secret");
AuthenticationResult result = await context.AcquireTokenAsync(resource, credential);
return result.AccessToken;
});
try
{
var connStrENTT = kvClient.GetSecretAsync("https://myvault.vault.azure.net/", "DBConfigConnection").Result.Value;
}
Why do you need to acquire token via your code if you are using managed identity? Managed identity is supposed to hide this for you.
Please use the guidance provided in a sample like this to take the correct steps.

Roles come back as null from Azure Analysis Services database when connecting using AMO in C#

I have a tabular Azure Analysis Services database with compatibility level 1400. When I connect and try to retrieve the roles using the AMO package the Roles property is always null, the same for the DatabasePermissions property that is mentioned in this answer.
I am using the Tabular.Server and Tabular.Database objects as recommended in the official docs.
I have based my code off this answer and I am connecting using an administrator account.
Proof that roles are setup on the DB that I am accessing:
Inspecting the database object:
Interestingly enough I have two other databases inside the same Azure Analysis Services server and they have the same issue.
My code:
using (Server server = new Server())
{
string serverDomain = "australiasoutheast.asazure.windows.net";
string serverName = "redacteddevpilotv1";
string databaseModel = "PilotV1";
string serverAddress = $"asazure://{serverDomain}/{serverName}";
//string token = await GetAccessToken($"https://{serverDomain}");
//string connectionString = $"Provider=MSOLAP;Data Source={serverAddress};Initial Catalog={databaseModel};User ID=;Password={token};Persist Security Info=True;Impersonation Level=Impersonate";
string connectionString = $"Provider=MSOLAP;Data Source={serverAddress};Initial Catalog={databaseModel};User ID=redacted;Password=redacted;Persist Security Info=True;Impersonation Level=Impersonate";
var t = server.SupportedCompatibilityLevels;
var x = server.Roles;
server.Connect(connectionString);
t = server.SupportedCompatibilityLevels;
x = server.Roles;
Database d = server.Databases.FindByName(databaseModel);
}
The documentation goes into how to add roles not how to retrieve them...
It turns out that instead of accessing roles through database.Roles I need to access them via database.Model.Roles. I'm not sure why this is or if it is documented anywhere but I was put onto this fact by another question.
After doing this I now have access to the ModelRole objects that I want.

Categories