Getting Azure App Service restarts as metric - c#

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);
}
}
}
}
}

Related

How to get a MSAL access token for SharePoint Online in a federated environment the non-interactive way in a non-interactive .Net console app?

The task as simple as to have a scheduled .NET console app which will download a file from SharePoint Online on a regular basis using AD domain user account.
If I use recommended way
var token = publicApplication.AcquireTokenByIntegratedWindowsAuth(scopes).ExecuteAsync().Result;
I'm getting
UriFormatException: Invalid URI: The hostname could not be parsed.
What does it mean? Which URI, hostname? Should I override something somewhere or add some special parameter?
I've googled thru this stuff a lot, and I have no idea where to look further, any advice will be appreciated.
P.S. I have no permissions to do anything on SharePoint side, I'm not a SP admin. I just have access to specific folder on the site from which I'm downloading the file. And also I have a code which works interactively:
WebRequest.DefaultWebProxy = WebRequest.GetSystemWebProxy();
WebRequest.DefaultWebProxy.Credentials = CredentialCache.DefaultNetworkCredentials;
var scopes = new string[] { "https://tenant.sharepoint.com/.default" };
var options = new PublicClientApplicationOptions()
{
TenantId = "tenant.com",
ClientId = "{872cd9fa-d31f-45e0-9eab-6e460a02d1f1}",//known Visual Studio Id
};
var publicApplication = PublicClientApplicationBuilder.CreateWithApplicationOptions(options).Build();
var token = publicApplication.AcquireTokenInteractive(scopes).WithLoginHint("name.surname#tenant.com").ExecuteAsync().Result;
But it shows a browser window
No questions asked, pop-up disappear, and I get the token which is used further to download a file from SPOnline using /_api/web/GetFileByServerRelativeUrl(' stuff.
So just run the app, see the popup, get the file downloaded. No interaction needed.
But this approach doesn't work if I put this routine really non-interactive:
Showing a modal dialog box or form when the application is not running in UserInteractive mode is not a valid operation. Specify the ServiceNotification or DefaultDesktopOnly style to display a notification from a service application.
Turns out the non-interactive way is only possible using tenant-side registered application. Implemented using certificate authentication.
But surprisingly the token obtained by ConfidentialClientApplicationBuilder doesn't work the way I wanted/expected (scopes/user impersonation issues). So now we use Graph client approach.
This is the only way which works for me (.NetFramework 4.7.2):
using Azure.Identity;
using Microsoft.Graph;
//...
static async Task GetFile(GraphServiceClient graphClient, string fileName2get)
{
var fileitem = graphClient
.Sites["SiteGuidYouMayGetBy /sites/[your site name]/_api/site/id"]
.Drives["CrazyLongDriveIdYouMayGetByEnumeratingDrivesHere"]
.Root
.ItemWithPath($"/Path To The File starting from Drive Root/{fileName2get}")
.Content
.Request().GetResponseAsync();
var stream = fileitem.GetAwaiter().GetResult();
using (var fileStream = System.IO.File.Create($"C:/Temp/{fileName2get}"))
{
await stream.Content.CopyToAsync(fileStream);
}
}

Workspace Domain-wide Delegation with service account not working for one project, works for others

We have different projects on GCP we use them to access different Google APIs. Most of them for internal use only.
In this particular case, we have 2 projects, both use Service Account and both are allowed on Workspace Domain-wide Delegation on the same scopes. They are almost clones of each other.
I execute a simple request with the same code (Spreadsheet.Get()) with project 1 credentials it works. I execute the same request with project 2 credentials it doesn't work.
Since Workspace Domain-wide Delegation it's activated the spreadsheet its shared to my email and I connect to the API with my email too (works with project 1 so this is not the problem) (impersonating a user)
The only difference it's that one project has OAuth Consent Screen on external (only 100 users cause we use it internally only, anyways..) and the other one it's internal but this has nothing to do with this right?
Where the problem could come from? Do I need to recreate the project that doesn't work?
Here is the error message :
Client is unauthorized to retrieve access tokens using this method, or client not authorized for any of the scopes requested
Edit to answer the comments but this code works depending on the service account we use
Generating the credentials:
internal static ServiceCredential GetApiCredentialsFromJson(string jsonCredentialsPath, string mailToMimic)
{
string jsonCertificate = File.ReadAllText(jsonCredentialsPath);
string privateKey = Regex.Match(jsonCertificate, #"(?<=""private_key"": "")(.*)(?="")").Value.Replace(#"\n", "");
string accountEmail = Regex.Match(jsonCertificate, #"(?<=""client_email"": "")(.*)(?="")").Value;
ServiceAccountCredential.Initializer credentials = new ServiceAccountCredential.Initializer(accountEmail)
{
Scopes = _scopes,
User = mailToMimic
}.FromPrivateKey(privateKey);
return new ServiceAccountCredential(credentials);
}
Using the credentials:
internal GoogleSheetService(ServiceCredential credentials)
{
SheetsService = new SheetsService(new BaseClientService.Initializer()
{
HttpClientInitializer = credentials
});
SheetsService.HttpClient.Timeout = TimeSpan.FromSeconds(100);
}
Client ID is allowed on the Drive, Ads and Spreadsheets scopes on the Workspace console.
The answer was simple, but we had to figure it out by ourselves.
The scopes you add in your app when you initialize the client need to be exactly the same scopes you added in the Google Admin wide-delegation page. Even if your app or part of your app don't need them all.
C# example:
private static readonly string[] _scopes = { DriveService.Scope.Drive, SheetsService.Scope.Spreadsheets, SlidesService.Scope.Presentations };
ServiceAccountCredential.Initializer credentials = new ServiceAccountCredential.Initializer(accountEmail)
{
Scopes = _scopes,
User = mailToMimic
}.FromPrivateKey(privateKey);
return new ServiceAccountCredential(credentials);
Here my app only needs SheetsService.Scope.Spreadsheets but I had to add DriveService.Scope.Drive and SlidesService.Scope.Presentations because the same client its used for other apps that need them.

Access application id of a system managed identity in Azure in C#

currently, we are developing an Azure app service application, which has a system managed identity assigned during setup of the app service. We use the managed identitiy with RBAC to access other Azure resources and that is working fine.
Now I want to get some information from the underlying managed identity to perform some checks. Especially I want to read the application id, which is assigned to this managed identity. I want to do that in C#. How can I access this information?
Any help appreciated. Thanks in advance.
Regards,
Stati
var credential = new DefaultAzureCredential();
string[] scopes = new string[] { "https://graph.microsoft.com/.default" };
var token = await credential.GetTokenAsync(new Azure.Core.TokenRequestContext(scopes));
var handler = new JwtSecurityTokenHandler();
var jsonToken = handler.ReadToken(token.Token) as JwtSecurityToken;
var appid = jsonToken.Claims.First(c => c.Type == "appid").Value;
Console.WriteLine(appid);

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.

How to connect to Cloud Firestore DB with .net core?

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

Categories