← Back to Dispatches

When Bureaucracy Meets Gambiarra: The Russian-Brazilian Way of Problem-Solving

How I built e-Boris — an Azure + GPT-4 email triage system named after a movie villain — to solve a process problem that defeated three engineers, a PM, and a consultant.

·8 min read
engineeringcultureautomationAI
gambiarra

After nearly six years in Brazil, my Portuguese is still… let’s call it “emergente” (my colleagues are too polite to say otherwise). But there’s one aspect of Brazilian culture I’ve mastered completely: the art of gambiarra.

For the uninitiated, gambiarra is that beautiful Brazilian concept that sits somewhere between “improvised solution” and “engineered chaos.” It’s the Russian из говна и палок (made of shit and sticks) with a tropical twist and considerably more optimism. It’s the engineering equivalent of looking at a problem that requires a $50,000 enterprise solution and saying “hold my caipirinha.” It’s the belezura of making things work when they absolutely shouldn’t.

The Graveyard of Empires (aka: That Email Process)

I won’t name names or point fingers - but imagine an organization where an email-based workflow had become the corporate equivalent of Afghanistan: where good intentions go to die, and every attempt at reform only makes things worse.

This process involved:

·        Multiple email threads (none of them labeled correctly)

·        Manual copy-pasting between systems

·        Prayer (legitimate step in the SOP)

·        More email threads

·        Existential dread

·        A final email confirming that yes, this is really how we do things

The real problem? Missing a critical email meant things didn’t get done. And in an avalanche of daily correspondence, critical emails had the camouflage skills of a jungle predator. My team and I needed to make sure nothing important slipped through the cracks.

People had tried to fix it. Senior engineers, project managers, even a consultant who charged enough to buy a small car. All failed. The process remained, immutable as a cockroach, twice as annoying.

Enter: e-Boris (Named After Boris the Blade, Naturally)

So I did what any reasonable engineer would do: I built a gambiarra and named it after a movie villain.

Meet e-Boris, named in tribute to Boris the Blade from Snatch - because like his namesake, this thing refuses to die, keeps coming back, and is surprisingly effective despite looking like it shouldn’t work.

The solution architecture is pure gambiarra elegance:

Azure Graph API watches my mailbox like a caffeinated hawk. Every incoming email triggers a subscription event.

The service account situation: So here’s where it gets interesting. I approached our tenant admins with what I thought was a perfectly reasonable request: “Hey, can my app have Mail.Read Application permissions?”

You know, just the ability to read every single email in every single mailbox across the entire tenant. Running unattended. Without user context. What could possibly go wrong? I was genuinely baffled when they looked at me like I’d just asked for the nuclear launch codes and unrestricted access to the CEO’s calendar.

The workaround? I delegated access to my own mailbox to a service account. MY service account. Created specifically for this task. Properly documented. With appropriate permissions scoped to exactly one mailbox - mine. Not elegant, perhaps, but functional. Gambiarra principle #1: when you can’t go through the door, become the door.

GPT-4.1 plays email bouncer: Each email gets fed through Azure OpenAI. I crafted a prompt that acts like an overly suspicious doorman at an exclusive club - it knows exactly what “important” looks like in our context, and it’s not easily fooled by subject lines like “URGENT!!!” or “Quick question.”

Microsoft Teams becomes the alarm system: When the AI decides an email matches our “this-actually-matters” criteria, it fires off a message to our Teams chat. Not a polite notification. An angry, annoying message. The kind that makes your phone buzz with the insistence of a mosquito at 3 AM.

The Gambiarra Components: A Technical Safari

gambiarra

Now, I’m not going to provide the entire codebase here. Any modern smart-ass LLM can generate boilerplate code, and I’ll spare you the soul-crushing details of configuring private endpoints, virtual networks, and all the other infrastructure theatre that makes cloud engineers feel important. Instead, let me walk you through the hoops I had to jump through - the parts where the gambiarra really shines.

What You’ll Need

1.      An Azure subscription (preferably one where you can explain the monthly charges)

2.      Patience (not available in the Azure Marketplace, unfortunately)

3.      A sense of humor about security policies

Part 1: Teaching Graph API to Snitch on Your Inbox

The Azure Graph API has this beautiful feature called subscriptions for monitoring resources. In our case, we want it to watch for incoming emails and immediately tattle on them.

The concept is simple: you tell Graph API “hey, watch this mailbox,” and whenever something happens, it calls your webhook. The reality is slightly more complex because Microsoft wants to make sure you’re serious about this relationship.

Authentication: The Delegated Dance

First things first - we need to authenticate. Remember, we’re using delegated permissions through a service account, not the god-mode application permissions that our tenant admins wisely denied us.

public GraphService(IOptions<EmailMonitoringConfiguration> config, ILogger<GraphService> logger)
{
    _config = config.Value ?? throw new ArgumentNullException(nameof(config));
    _logger = logger ?? throw new ArgumentNullException(nameof(logger));
  // Use UsernamePasswordCredential for delegated permissions with service account
    // This allows accessing delegated mailboxes using service account credentials
    _logger.LogInformation("Initializing GraphService with UsernamePasswordCredential for user: {Username}",        _config.ServiceAccountUsername);
    var credential = new UsernamePasswordCredential(
        _config.ServiceAccountUsername,
        _config.ServiceAccountPassword,  // Stored securely in Azure Key Vault, obviously
        _config.AzureTenantId,
        _config.AzureClientId);
    _graphClient = new GraphServiceClient(credential);
}

Important note for the security-conscious (hi again, Infosec team! 👋):

Yes, we’re using username/password authentication. No, the credentials are not hardcoded in the source. They live in Azure Key Vault where they belong, protected by managed identities, access policies, and several layers of “please don’t touch this” permissions. The service account has only the specific delegated permissions it needs - access to one mailbox. That’s it. Not the CEO’s calendar. Not the finance team’s shared inbox. Just. One. Mailbox.

Setting Up the Subscription:

[Function("SetupSubscription")]
public async Task<HttpResponseData> Run(
    [HttpTrigger(AuthorizationLevel.Function, "post")] HttpRequestData req)
{
    try
    {        
_logger.LogInformation("Starting subscription setup process");        
// Parse request body for custom notification URL (optional)
        var requestBody = await req.ReadAsStringAsync();
        var setupRequest = string.IsNullOrEmpty(requestBody) 
            ? new SetupSubscriptionRequest() 
            : JsonSerializer.Deserialize<SetupSubscriptionRequest>(requestBody) ?? new SetupSubscriptionRequest();
        // Build notification URL
        var notificationUrl = BuildNotificationUrl(req, setupRequest.CustomNotificationUrl);
        _logger.LogInformation("Setting up subscription with notification URL: {NotificationUrl}", notificationUrl);
     // Create the subscription
        var subscription = await _graphService.CreateEmailSubscriptionAsync(notificationUrl, _config.MonitoredMailbox);
        var responseData = new
        {
            success = true,
            subscriptionId = subscription.Id,
            expirationDateTime = subscription.ExpirationDateTime,
            notificationUrl = notificationUrl,
            resource = subscription.Resource,
            changeType = subscription.ChangeType,
            clientState = subscription.ClientState,
            message = "Email subscription created successfully"
        };
        // Send success notification to Teams
        await _teamsService.SendNotificationAsync(
            $"Email Subscription Created: Successfully created email subscription (ID: {subscription.Id}) with expiration: {subscription.ExpirationDateTime:yyyy-MM-dd HH:mm UTC}",
            Success);
        _logger.LogInformation("Subscription setup completed successfully. Subscription ID: {SubscriptionId}", subscription.Id);
        var response = req.CreateResponse(HttpStatusCode.OK);
        response.Headers.Add("Content-Type", "application/json");
        await response.WriteStringAsync(JsonSerializer.Serialize(responseData, /* ... */));
        return response;
    }
    catch (Exception ex)
    {
        _logger.LogError(ex, "Failed to set up email subscription");
        // Send error notification to Teams - because failures should be loud
        await _teamsService.SendNotificationAsync(
            $"Subscription Setup Failed: {ex.Message}",
            Error);        // ... error handling and response omitted for brevity
    }
}

The clever bits here:

1.      Dynamic notification URL: You can optionally pass a custom notification URL in the request body. Useful when testing with ngrok or when your Azure Function decides to change its hostname at 3 AM.

2.      Teams notifications for everything: Success? Teams gets notified. Failure? Teams gets notified. The subscription knows when it’s born, and it announces its death too. Your chat becomes a surveillance log of e-Boris’s lifecycle.

3.      That **** field: It’s returned in the response so you can verify it matches what you expect. This is your “trust but verify” moment with Microsoft.

The Subscription Renewal Dance:

Here’s a fun fact: Graph API subscriptions expire. Maximum 72 hours for mail subscriptions. So you need a function that runs periodically and tells Microsoft “yes, I still care about these emails.”

[Function("AutoRenewSubscriptions")]
public async Task RunTimer([TimerTrigger("0 0 * * * *")] TimerInfo timerInfo)
{
    _logger.LogInformation("Auto subscription renewal check triggered at: {DateTime}", DateTime.UtcNow); 
    try
    {
        // Check Graph connectivity before attempting renewals
        var isConnected = await _graphService.TestConnectivityAsync();
        if (!isConnected)
        {
            _logger.LogWarning("Microsoft Graph not accessible during renewal check");
            return; // Fail gracefully when Microsoft is having a bad day
        }
        // Retrieve all current subscriptions
        var subscriptions = await _graphService.GetSubscriptionsAsync();
        if (subscriptions == null || subscriptions.Count == 0)
        {
            _logger.LogWarning("No subscriptions found for renewal check.");
            return;
        }
        int renewalWindowHours = _config.SubscriptionRenewalWindowHours > 0 
            ? _config.SubscriptionRenewalWindowHours 
            : 24; // Default to 24 hours before expiration
var now = DateTime.UtcNow;
        int renewedCount = 0;
 foreach (var sub in subscriptions)
        {
            if (sub.ExpirationDateTime.HasValue && !string.IsNullOrEmpty(sub.Id))
            {
                var timeToExpire = sub.ExpirationDateTime.Value - now;      
 if (timeToExpire.TotalHours <= renewalWindowHours)
                {
                    try
                    {
                        _logger.LogInformation("Attempting to auto-renew subscription: {SubscriptionId}", sub.Id);
var renewed = await _graphService.RenewSubscriptionAsync(sub.Id);
                        renewedCount++;  
await _teamsService.SendNotificationAsync(
                            $"Auto-Renewed Subscription: {renewed.Id}. New expiration: {renewed.ExpirationDateTime:yyyy-MM-dd HH:mm UTC}",
                            Success);
                    }
                    catch (Exception rex)
                    {
                        _logger.LogError(rex, "Failed to auto-renew subscription: {SubscriptionId}", sub.Id);
                        await _teamsService.SendNotificationAsync(
                            $"Auto-Renewal Failed: Subscription {sub.Id} - {rex.Message}",
                            Error);
                    }
                }
            }
        }
        _logger.LogInformation("Auto-renewal process completed. Renewed {RenewedCount} subscriptions.", renewedCount);
 // Send summary health check notification only during business hours to avoid spam
        var currentHour = now.Hour;
        if (currentHour >= 8 && currentHour <= 18) // 8 AM to 6 PM UTC
        {
            await _teamsService.SendNotificationAsync(
                $"Subscription Health Check: Renewed {renewedCount} subscriptions at {now:HH:mm UTC}.",
                Info);
        }
    }
    catch (Exception ex)
    {
        _logger.LogError(ex, "Auto subscription renewal check failed");
await _teamsService.SendNotificationAsync(
            $"Auto Renewal Check Failed: {ex.Message}",
            Error);
    }
}

The clever bits:

1.      Configurable renewal window: Rather than hardcoding “renew 48 hours before expiration,” it’s configurable. Because production always teaches you that your assumptions were wrong.

2.      Connectivity check first: Before trying to renew anything, we check if Graph API is even reachable. No point in throwing exceptions if Microsoft is having an outage.

3.      Business hours notifications: The health check summary only notifies during business hours (8 AM - 6 PM UTC). Because nobody needs a “everything is fine” message at 3 AM. Error notifications? Those come through 24/7. Failures are always urgent.

4.      Per-subscription error handling: If one renewal fails, we keep going. Don’t let one bad subscription take down the whole renewal process.

Part 2: The Webhook - Where Security Theatre Meets Reality

Now here’s where it gets spicy. The webhook MUST be publicly accessible. Microsoft needs to reach it from the internet. Your security team will have opinions about this.

The Security Hoops:

1. Validation Token Dance: When you register the webhook, Graph API sends a validation request. You must immediately return the validation token. No processing. No thinking. Just echo it back.

 [Function("EmailSubscriptionWebhook")]
    public async Task<HttpResponseData> Run(
        [HttpTrigger(AuthorizationLevel.Anonymous, "post")] HttpRequestData req)
    {
        try
        {
            // Check if this is a validation request first
            var query = System.Web.HttpUtility.ParseQueryString(req.Url.Query);
            var validationToken = query["validationToken"];
            if (!string.IsNullOrEmpty(validationToken))
            {
                // This is a validation request
                return await HandleValidationRequestAsync(req);
            }
            else
            {
                // This is a regular notification
                return await HandleNotificationAsync(req);
            }
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error processing webhook request");
            var errorResponse = req.CreateResponse(HttpStatusCode.InternalServerError);
            await errorResponse.WriteStringAsync("Internal server error");
            return errorResponse;
        }
    }

2. Client State Validation: Every notification includes the ClientState you provided during subscription setup. Verify it. Always. This is your “password” that proves the request came from Graph API and not some bored hacker.

3. IP Allowlisting: If you’re feeling extra paranoid (you should be), you can restrict the webhook to only accept requests from Microsoft’s IP ranges. They publish these. They also change them. Good luck.

Part 3: Reading the Actual Email (The Delegated Permissions Way)

When the webhook fires, you get a notification with a resource URL. But here’s the catch -we’re using delegated permissions, not application permissions. So instead of , we need to explicitly specify which user’s mailbox we’re reading from (hint: it’s the one we delegated access to):

private async Task ProcessEmailNotification(ChangeNotification notification)
{
    var userPrincipalName = _config["MonitoredUserEmail"]; // The mailbox we're watching
            // Extract message ID from the resource path
            // Resource format is typically: "users/{userId}/messages/{messageId}"
            var resourceParts = notification.Resource.Split('/');
            // Check if this is a specific message notification
            if (resourceParts.Length >= 4 && resourceParts[2].Equals("messages", StringComparison.OrdinalIgnoreCase))
            {
                var userId = resourceParts[1];
                var messageId = resourceParts[3];    
    // Use direct message access by ID - this works with delegated permissions
    // and will find the message regardless of which folder it's in
    var message = await _graphClient.Users[userPrincipalName].Messages[messageId]
        .GetAsync(requestConfiguration =>
        {
            requestConfiguration.QueryParameters.Select = new[] 
            { 
                "id", "subject", "from", "receivedDateTime", 
                "bodyPreview", "toRecipients", "importance", "body" 
            };
        });
// Now we have the email. Time to ask GPT if we should panic.
    await AnalyzeWithAI(message);
}

Why this matters: With delegated permissions, you’re acting as a specific user (in this case, yourself) rather than as an omniscient application. The code explicitly states instead of just because the service account context needs to know exactly which delegated mailbox it’s accessing.

It’s the difference between “show me MY email” and “show me the email from the mailbox I’ve been given permission to access.” Subtle, but crucial when your tenant admins are watching. 👀

Part 4: Summoning GPT-4.1 to Judge Your Emails

This is where the magic happens. We feed the email to Azure OpenAI and let it decide if this email deserves to interrupt someone’s day:

private async Task AnalyzeWithAI(Message email)
{
    var prompt = $@"You are an email triage system. Analyze this email and determine if it requires immediate attention.
Subject: {email.Subject}
From: {email.From.EmailAddress.Address}
Body: {email.Body.Content}
Does this email match our critical criteria? Reply with YES or NO and brief reason.";
    var completion = await _openAiClient.GetChatCompletionsAsync(
        new ChatCompletionsOptions
        {
            Messages = { new ChatMessage(ChatRole.System, prompt) },
            Temperature = 0.3f, // We want consistency, not creativity
            MaxTokens = 150
        });
    var analysis = completion.Value.Choices[0].Message.Content;
    if (analysis.StartsWith("YES", StringComparison.OrdinalIgnoreCase))
    {
        await SendAngryTeamsMessage(email, analysis);
    }
}

Part 5: The Angry Teams Notification

When GPT decides an email is important, we need to yell about it in Teams. Graph API to the rescue again, just ask your friend Claude to complete the code:

/// Finds a Teams chat by name (topic) using Microsoft Graph API
public async Task<Chat?> FindChatByNameAsync(string chatName)

/// Sends a message to a Teams chat using Microsoft Graph SDK
public async Task SendTeamsMessageAsync(string chatId, string message)

The Supporting Cast

You’ll also want these utility functions to keep your gambiarra healthy:

ListSubscriptions: See what you’re actually monitoring (useful when you forget what you deployed last week)

HealthCheck: Verify everything still works (it probably doesn’t)

DeleteSubscription: For when you inevitably break something and need to start over

The Gambiarra Reality Check: It Works Until It Doesn’t

gambiarra

Let me be clear about something: this solution works. It works beautifully, catching important emails, yelling at people in Teams, and generally doing its job with the reliability of a Soviet-era Lada - which is to say, surprisingly well given what it’s made of.

But.

As with every gambiarra ever constructed, there’s a certain… fragility to the whole enterprise.

Things That Will Eventually Go Wrong

gambiarra

Microsoft will change something. They always do. One day you’ll wake up to a Teams notification that says “Auto-Renewal Failed” and you’ll discover that Microsoft decided to deprecate username/password authentication for service accounts, or change the Graph API endpoint structure, or require some new OAuth2 flow that involves sacrificing a chicken at midnight.

You will get electrocuted. Not literally (probably), but metaphorically. That public webhook? One day someone will scan it. Your logs will fill with validation attempts from IP addresses in countries you can’t pronounce. You’ll get a very polite email from your InfoSec team with the subject line “We need to talk.” They’ll use words like “attack surface” and “zero trust architecture” while looking at your gambiarra with the expression of someone who just found a nest of spiders in their coffee maker.

The service account password will expire. It’s in a policy somewhere. Three months, six months, whenever your organization decided is appropriate. The app will stop working at 2 AM on a Sunday. You’ll find out on Monday when someone asks “hey, did you see that email about the critical thing?” No. No, you did not.

Azure OpenAI will have opinions. GPT-4.1 will one day decide that every email is urgent. Or that no emails are urgent. Or it will start responding in haikus. The prompt that worked perfectly for six months will suddenly produce nonsense because OpenAI updated their model and didn’t tell anyone.

Graph API subscriptions will just… stop. For no reason. With no notification. The renewal function will run successfully, return a 200 OK, and absolutely nothing will happen. You’ll discover this when someone asks about that email you definitely didn’t see.

The Deployment Trap

And here’s where it gets really fun. You’ll think to yourself: “I should automate this deployment. I’ll ask Claude (or ChatGPT, or whatever smart-ass LLM is popular this week) to generate some PowerShell and Bicep templates for me.”

Don’t.

I mean, do it if you want. It’s a free country. But know that you’re about to embark on a journey that ends with you typing and watching in horror as the LLM-generated script cheerfully destroys your production resource group because it “helpfully” decided to clean up old resources first.

# What you asked for: "Deploy my Azure Function for email monitoring"
# What the LLM generated:
# Step 1: Clean up existing resources (THIS WILL DELETE EVERYTHING)
# Step 2: Deploy new infrastructure
# Step 3: ???
# Step 4: You're fired

The LLM will generate Bicep templates that look absolutely perfect. The syntax will be flawless. The structure will be beautiful. It will deploy successfully. And then you’ll discover it created your Function App in the wrong region, or forgot to configure the managed identity, or set the pricing tier to “Premium” because it assumed you want the best.

Pro tip: Test deployment scripts in a sandbox environment. Or better yet, write them yourself while the LLM watches and makes suggestions. Treat AI-generated infrastructure code the way you’d treat a recipe from a cookbook written by someone who has never actually cooked - with extreme skepticism and a fire extinguisher nearby.

Why Build It Anyway?

So why do this? Why build a gambiarra held together with delegated permissions, API subscriptions, and the sincere hope that nothing breaks on the weekend?

Because it solves the actual problem.

The “proper” solution would involve:

·        A six-month project timeline

·        Architecture review boards

·        Compliance assessments

·        A budget that could feed a small village

·        Enterprise service buses

·        Kubernetes clusters (for some reason, there’s always Kubernetes)

·        A vendor that promises “seamless integration” and delivers neither

By the time all that’s approved, the problem would have evolved into three new problems, and you’d still be in email hell.

The gambiarra? Built in a weekend. Deployed on Monday. Working by Tuesday. Maintained with occasional cursing and strategic application of duct tape.

The Brazilian Way

gambiarra

There’s a reason Brazilians have a word for this. It’s not just about making do with limited resources - it’s about recognizing that sometimes the “proper” solution is impossible, impractical, or will arrive three years too late to matter.

A gambiarra acknowledges reality: systems are messy, budgets are limited, time is short, and perfection is the enemy of working.

So yes, build your gambiarra. Document it (for the poor soul who inherits it). Monitor it. Keep a fire extinguisher handy. And when it inevitably breaks, fix it with more branches, more shit, and perhaps a little Azure Logic Apps if you’re feeling fancy.

Just remember: in the graveyard of enterprise processes, the gambiarra that actually works is worth more than the enterprise architecture that never ships.

Belezura.

P.S. Legal disclaimer: all companies, people, processes, and organizational dysfunctions described in this article are entirely fictional and exist solely in the author’s sick imagination. Any resemblance to real entities, living or dead, functional or catastrophically broken, is purely coincidental and should not be used as evidence in any compliance audit.

P.P.S. If you’re thinking of asking an LLM to generate deployment scripts based on this article: good luck. Send photos of the aftermath. I’ll be here with popcorn.