Skip to content

2025

Today I Learned: Triggering PowerAutomate Through Http Request

If you're working within the Microsoft ecosystem, you might have ran across a tool called PowerAutomate, this is Microsoft's low-code solution for building out simple automation (similar to tools like If This Then Than (IFTTT)).

A common use I've used with PowerAutomate is integrating with Teams to kick off a workflow when someone posts a message. For example, you could have a trigger listening for when someone posts in your support channel to auto-respond back with a canned message and tagging whoever your support person or support team is.

The problem I've ran into with PowerAutomate is that it has a steep learning curve and isn't very intuitive for engineers to pick up. As such, we typically use PowerAutomate for simpler flows (or if we need to integrate with another Microsoft product like Teams or Planner).

Recently, I was tackling a problem where we had an application that needed to post a message into a Teams channel. Back in the day, I would have use a webhook and published the message that way, however, Microsoft removed the ability to use Webhooks into Teams in late 2024. The main solution going forward is that you would have had to build a Teams application and install it for your tenant/channel. This can be a bit annoying if all you have is a simple workflow that you need to stand-up.

This got me thinking, I know I can use PowerAutomate to send a message to Teams, but can I trigger the flow programmatically (at this point, I was familiar with timers and when something happened).

Doing some digging, I found that PowerAutomate can trigger a workflow off of a HTTP Request.

Jackpot!

Even better, we can give the PowerAutomate flow a specific payload to expect as part of the request body, even better!

Combining these concepts, I can create a flow like the following:

First, let's set up a trigger that takes in a payload. In this case, the caller will give us the message content and a bit more info on which team and channel to post this message into.

Setting up the trigger

After setting up the trigger, we can then add a step for posting a message in Teams. Instead of using the pre-populated options that we get from our connection, we can use the values from the trigger instead.

Setting up the message step

After saving, our entire flow looks like the following

Full flow

From here, we can then use the URL that was created as part of the trigger and have our application invoke the flow.

Today I Learned: Configuring Git to Use A Specific Key for a Specific Repo

When working with a Git repository, there are two ways to authenticate yourself, either by using your name/password or by leveraging an SSH key, proving who you are. I like the SSH key the best since I can create the key for my machine, associate it to my profile, and then I'm good to go.

However, the problem becomes when I have to juggle different SSH keys. For example, I may have two different accounts (personal and a work one) and these accounts can't use the same SSH key (in fact, if you try to add the same SSH key to two different accounts, you'll get an error message).

In these cases, I'd like to be able to specify which SSH key to use for a given repository.

Doing some digging, I found the following configures the sshCommand that git uses

git config core.sshCommand 'ssh -i C:\\users\\<name>\\.ssh\\<name_of_private_key>'

Because we're not specifying a scope (like --global), this will only apply to the single repository.

Today I Learned: Configuring HttpClient via Service Registration

When integrating with an external service via an API call, it's common to create a class the encapsulates dealing with the API. For example, if I was interacting with the GitHub API, I might create a C# class that wraps the HttpClient, like the following:

public interface IGitHubService
{
    Task<string> GetCurrentUsername();
}

public class GitHubService : IGitHubService
{
    private readonly HttpClient _client;
    public GitHubService(HttpClient client)
    {
        _client = client;
    }
    public async Task<string> GetCurrentUsername()
    {
        // code implementation
    }
}

Repetition of Values

This is a great start, but over time, your class might end up like the following:

public class GitHubService
{
    private readonly HttpClient _client;
    public GitHubService(HttpClient client)
    {
        _client = client;
    }
    public async Task<string> GetCurrentUsername()
    {
        var result = _client.GetFromJsonAsync("https://api.github.com/user")
        return result.Login;
    }
    public async Task<List<string>> GetAllUsers()
    {
        var result = _client.GetFromJsonAsync("https://api.github.com/users");
        return result.Select(x => x.Login).ToList();
    }
    public async Task<List<string>> GetTeamNamesForOrg(string org)
    {
        var result = _client.GetFromJsonAsync($"https://api.github.com/orgs/{org}/teams");
        return result.Select(x => x.Name).ToList();
    }
}

Right off the bat, it seems like we're repeating the URL for each method call. To remove the repetition, we could extract to a constant.

public class GitHubService
{
    private readonly HttpClient _client;
    // Setting the base URL for later usage
    private const string _baseUrl = "https://api.github.com";
    public GitHubService(HttpClient client)
    {
        _client = client;
    }
    public async Task<string> GetCurrentUsername()
    {
        var result = _client.GetFromJsonAsync($"{_baseUrl}/user")
        return result.Login;
    }
    public async Task<List<string>> GetAllUsers()
    {
        var result = _client.GetFromJsonAsync($"{_baseUrl}/users");
        return result.Select(x => x.Login).ToList();
    }
    public async Task<List<string>> GetTeamNamesForOrg(string org)
    {
        var result = _client.GetFromJsonAsync($"{_baseUrl}/orgs/{org}/teams");
        return result.Select(x => x.Name).ToList();
    }
}

This helps remove the repetition, however, we're now keeping track of a new field, _baseUrl. Instead of using this, we could leverage the BaseAddress property and set that in the service's constructor.

public class GitHubService
{
    private readonly HttpClient _client;
    public GitHubService(HttpClient client)
    {
        _client = client;
        _client.BaseAddress = "https://api.github.com"; // Setting the base address for the other requests.
    }
    public async Task<string> GetCurrentUsername()
    {
        var result = _client.GetFromJsonAsync("/user")
        return result.Login;
    }
    public async Task<List<string>> GetAllUsers()
    {
        var result = _client.GetFromJsonAsync("/users");
        return result.Select(x => x.Login).ToList();
    }
    public async Task<List<string>> GetTeamNamesForOrg(string org)
    {
        var result = _client.GetFromJsonAsync($"/orgs/{org}/teams");
        return result.Select(x => x.Name).ToList();
    }
}

I like this refactor because we remove the field and we have our configuration in one spot. That being said, interacting with an API typically requires more information than just the URL. For example, setting up the API token or that we're always expecting JSON for the response. We could add the header setup in each method, but that seems quite duplicative.

Leveraging Default Request Headers

We can centralize our request headers by leveraging the DefaultRequestHeaders property and updating our constructor.

public class GitHubService
{
    private readonly HttpClient _client;
    public GitHubService(HttpClient client)
    {
        _client = client;
        _client.BaseAddress = "https://api.github.com";
        _client.DefaultRequestHeaders.Add("Accept", "application/vnd.github+json");
        _client.DefaultRequestHeaders.Add("Authentication", $"Bearer {yourTokenGoesHere}");
        _client.DefaultRequestHeaders.Add("X-GitHub-Api-Version", "2022-11-28");
    }
    public async Task<string> GetCurrentUsername()
    {
        var result = _client.GetFromJsonAsync("/user")
        return result.Login;
    }
    public async Task<List<string>> GetAllUsers()
    {
        var result = _client.GetFromJsonAsync("/users");
        return result.Select(x => x.Login).ToList();
    }
    public async Task<List<string>> GetTeamNamesForOrg(string org)
    {
        var result = _client.GetFromJsonAsync($"/orgs/{org}/teams");
        return result.Select(x => x.Name).ToList();
    }
}

I like this refactor because all of our configuration of the service is right next to how we're using it, so easy to troubleshoot. At this point, we would need to register our service in the Inversion of Control (IoC) container and then everything would work.

Generally, you'll find this in Startup.cs and would look like:

services.AddTransient<IGitHubService, GitHubService>();

An Alternative Approach for Service Registration

However, I learned that when you're building a service that's wrapping an HttpClient, there's another service registration method you could use, AddHttpClient with the Typed Client approach.

Let's take a look at what this would look like.

1
2
3
4
5
6
7
8
// In Startup.cs

services.AddHttpClient<IGitHubService, GitHubService>(client => {
    client.BaseAddress = new Uri("https://api.github.com");
    client.DefaultRequestHeaders.Add("Accept", "application/vnd.github+json");
    client.DefaultRequestHeaders.Add("Authorization", $"Bearer {apiTokenGoesHere}");
    client.DefaultRequestHeaders.Add("X-GitHub-Api-Version", "2022-11-28");
});

We've essentially moved our configuration logic from the GitHubService to the IoC container, simplifying the service.

public class GitHubService : IGitHubService
{
    private readonly HttpClient _client;
    public GitHubService(HttpClient client)
    {
        _client = client;
    }
    public async Task<string> GetCurrentUsername()
    {
        var result = _client.GetFromJsonAsync("/user")
        return result.Login;
    }
    public async Task<List<string>> GetAllUsers()
    {
        var result = _client.GetFromJsonAsync("/users");
        return result.Select(x => x.Login).ToList();
    }
    public async Task<List<string>> GetTeamNamesForOrg(string org)
    {
        var result = _client.GetFromJsonAsync($"/orgs/{org}/teams");
        return result.Select(x => x.Name).ToList();
    }
}

My Thoughts

Even though this is a new approach, I'm kind of torn if I like it or not. On one hand, I appreciate that we can centralize the logic in one spot so that everything for the GitHubService is one spot. However, if we needed other dependencies to configure the service (for example, we needed to get the bearer token from AppSettings), I could see this getting a bit more complicated, though contained.

On the other hand, we could shift all that config to the IoC and let it deal with that. It definitely streamlines the GitHubService so we can focus on the endpoints and their logic, however, now I've got to look for two spots to see where the client is being configured.