# Welcome!

What's a GigaCode?

## Hello, World!

Hi! I'm Brandon. To those of you who already know me, welcome! To those who don't, here's a "who am I" speedrun:

* My name is Brandon
* I work in the MSP (Managed Service Provider) Industry
* You may know me from my work at eTop Technology
* Originally from Arizona, now living in California
* Outside of tech, I enjoy making/performing music and general "Maker" stuff (3D Printing, various electronics projects, resurrecting dead old hardware)

## What to expect here

I've spent a lot of time working on new and exciting tech, many of which don't have great step-by-step guides or content about the "why" of how things work. If I've already done the work to understand, implement, and promote new solutions, why should anyone else have to go through the same work? That's the spirit of this blog. I'll be writing about stuff I find super cool and exciting, and providing you the knowledge to implement it on your own.

## Getting in touch

Best way to reach me is via <giga@gigacode.dev>. Shoot me an email any time! You'll also find me on Discord as @gigacode, Mastodon as @<gigacode@infosec.exchange>, and Threads as @gigacodedev.&#x20;

{% hint style="info" %}
I do not have a Twitter account!
{% endhint %}


# Cybersecurity


# In the Wild - Abusing JWT Encoding

In the course of securing and supporting ourselves and our customers, we often come across new TTPs, or Tactics Techniques and Procedures, as they become used. As the defenders, *we* are the guinea pigs the threat actors test new stuff out on. This post reviews a real-life detection of what may be a new technique to further aid with ongoing efforts to evade URL sandboxing and defense. Data in this post may be obfuscated to protect our organization.

## Initial Discovery

Typically, my day starts with reviewing the general security state of our organization. This includes, among other tasks, reviewing our Microsoft Defender Incidents and Alerts to identify any detections overnight that weren't immediate enough for our security partner to raise the alarm. As such, these are typically user reported spam emails and Defender for 365 captured Phishing emails. These sorts of alerts can sit until the morning as Defender has already Quarantined or soft deleted the message after its Automated Investigation and Response (AIR) investigation. This is where the fun started

### The Incident

This particular incident did not seem out of the ordinary. Multiple phishing emails were sent to key individuals at our organization, Defender for 365 nabbed one, determined it to be malicious, and then the ZAP (Zero-hour Auto Purge) feature quickly removed all copies that had been delivered already. Typically, the extent of the review at this point is to document the alert, quickly spot check the AIR logs to make sure there's no further pending actions and that all entities were addressed, and then close out the alert. However, something caught my eye...

### The URL

The URL in the mail entity detection was similar to the following:

`https://drip[.]la/c/eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJtb25vbGl0aCIsImlhdCI6MTY5NTc3Nzc2NSwiZXhwIjoxNzI3MzEzNzY1LCJhdWQiOiJkZXRvdXIiLCJzdWIiOiJkZXRvdXJfbGluayIsImFjY291bnRfaWQiOiI1MTY1ODE2ODEiLCJ0cmlnZ2VyX2lkIjoiOTQ4OTI4Mjk3NTE2NTEiLCJkeW5hbWljX3VybCI6Im5vX3VybF9mb3JfeW91IiwidXJsIjoibmljZV90cnkifQ.I2NvAWEujTf7ednkh6RLujdzfyzDmPmbmrZWshAXaWY`

Notice anything familiar? That URL slug certainly looks like an authentication token to me. But why would an attacker be sending us a token? Isn't it usually the other way around? This caught my eye, and my morning was quickly sidetracked

## Understanding the Technique

{% hint style="info" %}
Already familiar with JWTs?[ Jump ahead to when we get back to our detection!](#our-phishing-url)
{% endhint %}

### What's a JWT anyways?

A JWT, or JSON Web Token, is a way to format and encode data for the purposes of transmitting information over the internet as a JSON object. This string of characters is URL safe (doesn't require any additional encoding) and can easily be inserted into a URL. The standard itself is defined in [RFC7519](https://datatracker.ietf.org/doc/html/rfc7519).

Due to the resulting object being relatively compact and in a common and consistent format, this is often used to transmit authentication claims between services. If you've ever made an OAUTH2.0 request, you've seen this format before. However, as we will discover, JWTs can be crafted to transmit really any information formatted as JSON.

### How does a JWT work?

A JWT has three main components:&#x20;

* Header, containing information about the token itself
* Payload, containing the data to communicate formatted as JSON
* Signature, validating the message was not modified and sometimes verifying the sender

Each of the components is separated by a `.`, so a token typically looks like:

`header.payload.signature`&#x20;

Let's take a look step by step at each component:

#### The Header

Typically, the header contains two components: A `typ` value to inform the receiving end what this is, and if signed, a `alg` value notating the algorithm used to generate the signature. The most common algorithms are HMAC SHA256 (symmetric) and RSA (asymmetric). We'll start crafting a custom JWT by starting here:

```json
{
    "typ": "JWT",
    "alg": "HS256"
}
```

This states we are a JWT, and we are signed with HMAC SHA256. This is then encoded using Base64 URL Encoding, which is similar to Base64 but with a few URL unsafe characters changed. `+` is changed to a `-`, `/` is replaced with a `_`, the final `==` padding is omitted, and the rest is the same. This gives us a result of `eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9` for our header.

#### The Payload

Next up is the meat of the request contaning our claims. While the standard from IETF for JWTs doesn't specify any claims as mandatory, they do specify several "registered" claim names to support interoperability. Generally, at minimum you will see the following claims in a JWT:

* `iss`: Issuer. This is the principal that issues the JWT
* `sub`: Subject. This is the principal that the JWT is providing information about
* `aud`: Audience. This is the recipient (or recipients) of the JWT
* `exp`: Expiration. This is an integer representing the seconds since Unix Epoch to denote when the token expires. This often includes some leeway for clock drift
* `nbf`: Not Before. This is an integer representing the seconds since Unix Epoch to denote when the token takes effect
* `iat`: Issued At. This is an integer representing the seconds since Unix Epoch to denote when the token

In addition to the registered claims, custom claims can also be created to pass along additional data. In the context of authentication, this is often data such as the scope of access. But it can be any valid JSON key/value pair. We'll make one up for this example, and put everything together as:

```json
{
    "iss": "gigacode",
    "sub": "blogUser",
    "aud": "gitbook",
    "exp": 1695977765,
    "nbf": 1695801647,
    "iat": 1695801647,
    "anyKeyWeWant": "AnyValue"
}
```

Using the same Base64 Web Encoding as the header, our payload is `eyJpc3MiOiAiZ2lnYWNvZGUiLCJzdWIiOiAiYmxvZ1VzZXIiLCJhdWQiOiAiZ2l0Ym9vayIsImV4cCI6IDE2OTU5Nzc3NjUsIm5iZiI6IDE2OTU4MDE2NDcsImlhdCI6IDE2OTU4MDE2NDcsImFueUtleVdlV2FudCI6ICJBbnlWYWx1ZSJ9`

#### The Signature

Finally, we need to create a signature. We'll use HMAC, or Hash-Based Message Authentication Code, to compute a hash using a symmetric secret key `gigacode`. We'll calculate the hash based off our plaintext token (`header.payload`) and encode the result as base64. This gives us a hash of `He2rSRvZpmMJmOgpLApGr3uurTNrKXy922FuOPiinq4`

#### Putting it All Together

With the signature, we're now ready to craft our full token. Separating each component with a `.`, we end up with `eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiAiZ2lnYWNvZGUiLCJzdWIiOiAiYmxvZ1VzZXIiLCJhdWQiOiAiZ2l0Ym9vayIsImV4cCI6IDE2OTU5Nzc3NjUsIm5iZiI6IDE2OTU4MDE2NDcsImlhdCI6IDE2OTU4MDE2NDcsImFueUtleVdlV2FudCI6ICJBbnlWYWx1ZSJ9.He2rSRvZpmMJmOgpLApGr3uurTNrKXy922FuOPiinq4`

#### Decoding and Validating

Let's act as the receiving end now, we just got the above JWT and want to know what it means. First, let's take the header and payload portions alone, and then run them through a base64 decoder. We get:

```json
{
  "typ": "JWT",
  "alg": "HS256"
}.{
  "iss": "gigacode",
  "sub": "blogUser",
  "aud": "gitbook",
  "exp": 1695977765,
  "nbf": 1695801647,
  "iat": 1695801647,
  "anyKeyWeWant": "AnyValue"
}
```

Looks good! Now lets make sure nothing got tampered with and the token is valid. Using our secret key `gigacode`, we'll do the same signature generation. This gives us a match! This is a valid token.

### Our Phishing URL

Back to our phishing URL, let's decode this JWT value in the slug. Running through a base64 decoder, we get the (slightly modified for publishing) result of:

```
{
  "alg": "HS256"
}.{
  "aud": "detour",
  "iss": "monolith",
  "sub": "detour_link",
  "iat": 1695659124,
  "nbf": 1695659124,
  "account_id": "[redacted]",
  "trigger_id": "[redacted]",
  "dynamic_url": null,
  "url": "https://login-office365[.]cloud"
}.[Signature]
```

Very interesting! So, what does this mean? Let's pick things apart.

#### Header

The header is fairly standard, with an HMAC SHA256 algorithm for the signature specified. It is missing the `typ` parameter, however, while recommended this is listed as optional in the IETF standard

#### Payload

The payload contains the usual audience, issuer, and subject. Interestingly, it also contains values for an issue and not valid before timestamp. These are not required, so it is curious they would be included. It's hard to tell for certain why they are there, whether the attackers are using an off the shelf tool to generate this and its easier just to keep it there or their web server actually utilizes this parameter. Next up though, we have the fun bits.

`account_id` and `trigger_id` are integer values that were different between the email addresses that received the detection. These are likely used so the attackers can correlate users sent the phish to who clicked the link, regardless of if information was entered. This kind of tracking is commonly observed across multiple phishing methods, and often leads to the subject being marked as an easy target for future attacks. In all instances observed, `dynamic_url` remained `null`. This is still very interesting and could indicate the attackers can craft custom redirect URLs for further evasion and/or social engineering. For example, in a spear phishing attack, this may include an organziations name or a url attribute specific to their login system to make the request appear more legitimate. Lastly, we have `url`, which is the URL to redirect the user to.

#### Signature

As we only have a hash to go off, we are unable to derive the secret key. However, it is safe to assume this would be a valid request.

## Investigating the Web Server

### Investigation Defenses

As with many instances of attacker-controlled webservers, some basic steps are taken to prevent investigation. Chiefly, if an attempt is made to directly navigate to the malicious domain, the webpage will simply exit itself. A generic 404 response is returned when any other path is attempted to be accessed. Further, a scan of the IPs linked to the malicious domain names show the service `awselb/2.0` on ports 80 and 443, indicating the domain resolves to an AWS Elastic Load Balancer at the edge. This essentially presents the same challenges as a reverse proxy, as the traffic is routed internally to its resource(s) assigned when the port listener receives traffic at the edge.

### IoCs

| Type   | IoC                      |
| ------ | ------------------------ |
| Domain | drip\[.]la               |
| Domain | login-office365\[.]cloud |
| IP     | 34.234.235\[.]177        |
| IP     | 54.82.80\[.]171          |
| IP     | 3.228.200\[.]240         |
| IP     | 50.19.188\[.]48          |
| IP     | 18.205.79\[.]17          |


# Cloud Concepts

"Old man yells at cloud"

<figure><img src="https://imgs.xkcd.com/comics/the_cloud.png" alt=""><figcaption><p>XKCD 908 - The Cloud</p></figcaption></figure>

## What will I find here?

I'll be organizing all my Cloud focused concepts here, and I expect this to be the most content rich section (at least starting out). I personally define cloud related items as really anything that isn't running on a local system, so this will include most Microsoft Online services such as Azure and 365. This section will be focused specifically on the configuration and management of cloud platforms. I expect some crossover of general development concepts, but ideally would like to have a section separately for the purely dev focused writings.


# Microsoft Azure

Because Building Your Own Data Center Isn't a Weekend Project

<figure><img src="/files/00XP6ITVwfiGUid0tLNu" alt="Microsoft Azure Logo" width="188"><figcaption><p>Cool logo, right?</p></figcaption></figure>

## What's an Azure?

Azure is Microsoft's cloud platform. There are *A LOT* of services you'll find there, from computing to storage, from security to DevOps, and everything in between. The best part? Most services are free or incredibly low cost at the volume most individuals and small businesses would use.&#x20;

{% hint style="info" %}
While Azure can be a cost effective platform, it can also be easy to accidentally rack up significant costs if resources are not provisioned and/or scoped properly. If you are new to the platform, I highly recommend reviewing the Intro to Cost Management guide
{% endhint %}


# Cognitive AI Services

Why think when you can make a computer think for you!


# Speech Services

This ain't your normal Microsoft Sam voice

The first component of Azure's Cognitive AI services we'll be covering is speech! Here's an overview of the services:

* Text to Speech
* [Speech to Text](/technology/cloud-concepts/microsoft-azure/cognitive-ai-services/speech-services/speech-to-text)
* Speech Translation
* Intent Recognition
* Speaker Recognition
* Keyword Recognition

Read on to see how to implement these in practice!


# Speech to Text


# Choosing Transcription Methods

## What's the difference?

When transcribing speech to text, Azure provides two methods: Real-time and Batch. Both have pros and cons depending on use-case:

**Real-time Transcription**

Real-time transcription, as the name suggests, transcribes audio as it's happening. This method is typically used for live events, meetings, or any scenario where immediate transcription is required. It allows applications to provide live captions for streaming media, transcribe phone conversations, and enable voice assistants to create natural, human-like conversational interfaces.

The primary advantage of real-time transcription is its immediacy; it can provide near-instantaneous transcriptions, making it ideal for live events or interactive applications. However, the downside is that the accuracy of real-time transcription might be slightly lower compared to batch transcription due to the time constraint. Also, handling large volumes of data in real time can be more resource-intensive.

**Batch Transcription**

On the other hand, batch transcription is used to transcribe a large amount of audio in storage. In this method, you point to audio files with a URI and asynchronously receive transcription results. This is particularly useful for applications that need to transcribe audio in bulk, such as transcriptions, captions, or subtitles for pre-recorded audio.

The main advantage of batch transcription is that it can handle a large number of submitted transcriptions concurrently, reducing the overall turnaround time. It also allows for more intensive processing, which can lead to higher accuracy, especially for complex or specific language. However, the downside is that batch transcription is not immediate; you must wait for the transcription to complete before you can access the results.

We'll be starting off with an example project to transcibe call recordings and provide brief summaries, so we need to transcribe multiple files at once where accuracy is important, and it doesn't need to happen as the speech is occurring. This is a perfect use-case for batch transcription, so I'll start by focusing on that method.&#x20;


# Provisioning your Cognitive Speech Services

Before we start making use of these services, we need to provision them first! This is a relatively quick process:

1. In your Azure Portal, navigate to the Resource Group that will be housing your cognitive services API deployment
2. Select the Create button<br>

   <div align="left"><figure><img src="/files/XfWsNhUpDVPpA4k4xybU" alt="" width="563"><figcaption></figcaption></figure></div>
3. Search for "Speech" and proceed to create the resource\
   ![](/files/viw0ZVVX5VCk5OllGECn)
4. Set the deployment options<br>

   <figure><img src="/files/nAb5oBBlWXlzGxP0LzqS" alt=""><figcaption></figcaption></figure>

   **Be mindful of the pricing tier. The free SKU does&#x20;*****not*****&#x20;currently support batch transcriptions. While the standard S0 tier will cost you some money, the rate for speech transcription is currently about $0.0003 per second, or $0.016 per minute, which should be pretty negligible for our usage**
5. Proceed directly to the Review + Create screen and provision the resource


# Batch Transcription

Consider the following scenario:

* Your job as service desk manager is to keep the wheels on the helpdesk spinning smoothly. Part of that is ensuring verbal phone conversations are following company policy and are polite
* You need to perform this task once per week, and even with a random selection of calls, it still can take 1-2 hours to review
* The end result of this is a simple summary of the call and general sentiment. You realize this can be automated!

With the above criteria, we have what we need to start automating! But let's get a handle on how this API works first

### Gathering recordings

Before we process speech, we need something to give the API to process. With batch transcriptions, you must provide a URI to download the call audio from. In practice, you would want to point this to the recordings endpoint of your calling software that houses the recordings. For the purposes of demonstration, I used AI Text-to-Speech to make a demo call and published it to a publicly available storage blog for ease of access. [You can listen to it here](https://gigalabstorage.blob.core.windows.net/call-audio/DemoCall.wav). We can use this same link to feed the audio into the speech service

## Step 1: Authentication

We'll be using our API keys for this demonstration. These can be found in the Keys and Endpoint section of your Speech Service in the Azure portal:<br>

<figure><img src="/files/EsP0JzZrIpCnuSjtFPeM" alt=""><figcaption></figcaption></figure>

Copy Key 1 somewhere safe for now. We need a secure way to get these keys, so let's use a KeyVault!

1. Provision the keyvault resource. Similar to the speech service, click the Create button in your Resource Group, then search for and choose Key Vault\
   ![](/files/Ef1MonFmXDksBd5UOtFe)
2. Configure the deployment details on the Basics page<br>

   <figure><img src="/files/ZW7ukhpfn0M8tqDohre2" alt=""><figcaption></figcaption></figure>

   Similar to the Speech service, the cost of a KeyVault is very small. 10,000 secrets transactions, much more than we will need, only costs $0.03. The Standard pricing tier is sufficient for most things.
3. Proceed to Review + Create, then create the resource
4. In your keyvault, head to the secrets page and select Generate/Import<br>

   <figure><img src="/files/bgNCoB9RNfezMdtnHIMe" alt=""><figcaption></figcaption></figure>
5. Enter the name and paste your API key into the Secret Value box. Ensure the secret is enabled and click Create

### Authenticating to our KeyVault

Now that we've securely stored our key somewhere we can pull from, we need a way to authenticate to the vault itself. If you've worked with Azure Service Principals, these steps should be familiar

1. In the Entra Identity portal, select Applications, then App Registrations. Create a New Registration\
   ![](/files/Aj5MLoNMBinLBWzb8Jb2)\
   ![](/files/XvY3iMeqlLxtFYje1tZ5)

2. Enter the name, select the Single Tenant account type, and create the registration<br>

   <figure><img src="/files/gTYlmXmH84ueNEu4qCfk" alt=""><figcaption></figcaption></figure>

3. In the app registration, go to the Secrets page, then create a new secret<br>

   <figure><img src="/files/uGfcgEZ9brswkgwte1vo" alt=""><figcaption></figcaption></figure>

   **This secret will not be displayed again! Save it somewhere safe**

4. Go to the overview and also note down the Application (client) ID and the Directory (tenant) ID. I've saved all as environment variables in my API interaction tool, Insomnia<br>

   <figure><img src="/files/TwDf4rqbchBJKEXe2OfG" alt=""><figcaption></figcaption></figure>

5. Lastly, we need to authorize this app registration to get secrets from our KeyVault. In the KeyVault, go to the Access Control page and add a new role assignment<br>

   <figure><img src="/files/LlUEJwrgsRRKfFuUAwDv" alt=""><figcaption></figcaption></figure>

6. Select the role Key Vault Secrets User, then add the Service Principal we just created<br>

   <figure><img src="/files/srI6o6HcEBzX7XVxGO9z" alt=""><figcaption></figcaption></figure>

## Step 2: Creating the Batch job

First up, we need our API key to interact with the service. Let's get it from our KeyVault. Using our API tool, we can send the following request to get an access token:

```sh
curl --request POST \
  --url https://login.microsoftonline.com/YourTenantId/oauth2/v2.0/token \
  --header 'Content-Type: application/x-www-form-urlencoded' \
  --data client_id=YourClientID \
  --data 'client_secret=YourSecret' \
  --data grant_type=client_credentials \
  --data scope=https://vault.azure.net/.default
```

The response should contain access\_token, token\_type, and expiration values. Note the access token down.

```json
{
	"token_type": "Bearer",
	"expires_in": 3599,
	"ext_expires_in": 3599,
	"access_token": "abc123"
}
```

We can now request our secret from the vault by using the following request:

```
curl --request GET \  
  --url 'https://{VaultURL}/secrets/{SecretName}?api-version=7.4' \
  --header 'Authorization: Bearer {YourAccessToken}'
```

The response should contain a "Value", which is the secret. Note this down

```
{
	"value": "abc123",
	"id": "https://{VaultURL}/secrets/{SecretName}/{SecretVersion}",
	"attributes": {
		"enabled": true,
		"created": {UNIXTimeStamp},
		"updated": {UNIXTimeStamp},
		"recoveryLevel": "Recoverable+Purgeable",
		"recoverableDays": 90
	},
	"tags": {}
}
```

Now that we have our authentication, let's ask the Speech API to make us a new batch job. The endpoint you need to send it to is based on the region the service was deployed in. For example, I deployed to US West 3, so my endpoint URL starts with `https://westus3.api.cognitive.microsoft.com`. You can find this on the overview of your provisioned service.&#x20;

\
We want to ask the Transcriptions service to do something, so the full URL should look like this: `https://westus3.api.cognitive.microsoft.com/speechtotext/v3.1/transcriptions`. The first part tells it what region to look in for our service, what service we'll be using, the API version, and finally the task we want to happen.

The body for this request should be formatted as:

```json
{ 
    "contentUrls": [
        "URLToRecording"
    ], 
    "locale": "en-US", 
    "displayName": "UniqueName", 
    "model": null, 
    "properties": { 
        "wordLevelTimestampsEnabled": true
    }
}
```

This sets the property for our batch transcription job. If we wanted to provide multiple URLs they can be added to the `contentUrls` array. Properties sets the properties of the job itself, be sure to add any additional `candidateLocales` if needed.

{% hint style="info" %}
The display name of the batch job must be unique. Since we will be pulling these via an automation once implemented to your favorite RPA platform, we can simply use a UNIX timestamp to ensure a unique name
{% endhint %}

Lastly, we need to auth the request. The API key we grabbed from KeyVault should be put in a header named "Ocp-Apim-Subscription-Key". All Togther, the request looks like:

```sh
curl --request POST \
  --url https://{Region}.api.cognitive.microsoft.com/speechtotext/v3.1/transcriptions \
  --header 'Content-Type: application/json' \
  --header 'Ocp-Apim-Subscription-Key: {API KEY}' \
  --data '{
  "contentUrls": [{ContentURLs}],
  "locale": "en-US",
  "displayName": "{UniqueName}",
  "model": null,
  "properties": {
    "wordLevelTimestampsEnabled": true
  }
}'
```

Then we can expect a response of:

```json
{
  "self": "https://{Region}.api.cognitive.microsoft.com/speechtotext/v3.1/transcriptions/{GUID}",
  "model": {
    "self": "https://{Region}.api.cognitive.microsoft.com/speechtotext/v3.1/transcriptions/{GUID}"
  },
  "links": {
    "files": "https://{Region}.api.cognitive.microsoft.com/speechtotext/v3.1/transcriptions/{GUID}/files"
  },
  "properties": {
    "diarizationEnabled": false,
    "wordLevelTimestampsEnabled": true,
    "displayFormWordLevelTimestampsEnabled": false,
    "channels": [
      0,
      1
    ],
    "punctuationMode": "DictatedAndAutomatic",
    "profanityFilterMode": "Masked"
  },
  "lastActionDateTime": "2023-09-02T21:02:04Z",
  "status": "NotStarted",
  "createdDateTime": "2023-09-02T21:02:04Z",
  "locale": "en-US",
  "displayName": "{Name}"
}
```

The important bits here are the `displayName` and the top level `self` link. Note them down.

Now we've created and started our batch job, and we want to make sure it's finished. By running a simple GET request against the `self` URL, we will receive a report for the batch job. This will contain all the above attribute, but with the addition of a `status`. This `status` string will show `Succeeded` once processed. Once successful, you'll also see a new Files object with a new URL. Query the batch job until it shows Successful:

```sh
curl --request GET
--url {SelfUrl}
--header 'Ocp-Apim-Subscription-Key: {API Key}'
```

Now we can check where our content can be found for this transcription. Query the same URL, but with `/files` appended

```sh
curl --request GET
--url {SelfUrl}/files
--header 'Ocp-Apim-Subscription-Key: {API Key}'
```

Your response should look like:

```json
{
    "values": [
        {
            "self": "https://{region}.api.cognitive.microsoft.com/speechtotext/v3.1/transcriptions/{GUID}/files/{GUID}",
            "name": "contenturl_0.json",
            "kind": "Transcription",
            "properties": {
                "size": 67095
            },
            "createdDateTime": "2023-09-02T21:02:21Z",
            "links": {
                "contentUrl": "{Results URL}"
                }
            },
            {
            "self": "https://{Region}.api.cognitive.microsoft.com/speechtotext/v3.1/transcriptions/{GUID}/files/{GUID}",
            "name": "report.json",
            "kind": "TranscriptionReport",
            "properties": {
                "size": 222
            },
            "createdDateTime": "2023-09-02T21:02:21Z",
            "links": {
                "contentUrl": "{Report URL}"
            }
        }
    ]
}
```

With that, we're just about done! Very last step is to actually get the results. Query the contentURL for the `contenturl_0.json` file and inspect the results. You'll see an object for CombinedRecognizedPhrases, then one for `lexical` within that - That's our transcription!

{% code overflow="wrap" %}

```
"hello it support desk how may i help you today hi my computer says i need to apply an update for my new software but i need administrator access can you provide that for me sure i will remote into your pc in and take care of this thank you if you watch what happens when i click the update it brings up this prompt asking you to confirm you need administrator access for this application go ahead and click the next button for me ok perfect now you'll see a change to an approval countdown ok got it and now it shows approved going forward it will automatically allow you to run this update application as an administrator this process works on all other applications so you can send in a request yourself next time as well or we'd be happy to walk you through it again if needed ok that makes sense thank you is there anything else i can help you with today while we are connected no that is all thank you very much you're welcome have a great day you too bye bye bye"
```

{% endcode %}

## Next steps

As you can see, that output is not quite human readable. But that's OK, now we can do a number of things to this output to make it something useful! Now that we have a transcript, we can send it to an AI Language Model like GPT-4 to create a summary, for example. That's a bit outside the scope of this doc, but here's what you can expect as an output:

{% code overflow="wrap" %}

```
In this call, the customer needed administrator access to update new software on their computer. The IT support agent remotely accessed the customer's PC to assist with the process. The agent demonstrated how to gain administrator approval for the software update and confirmed that the change was successful. The agent also informed the customer that this approval process can be applied to other applications and that the customer could either send in a request themselves in the future or seek assistance again. The customer had no further issues and expressed their gratitude before ending the call. Overall, the issue was efficiently resolved.
```

{% endcode %}


# Cost Management

What do you mean I owe Microsoft $20,000?

Nobody likes surprise bills. Check out the content in this section to understand Azure's billing concepts and tips to save money!


# Intro to Azure Cost Management

Because money doesn't grow on trees

## How does Azure pricing work?

### Consumption Based Pricing

Consumption based pricing is not unique to Azure, and most cloud services use it. On a basic level, you only pay for what you use. Whether that be hours a compute resource is running, the number of executions of a function, or total GB of storage used, for example. This is typically referred to as a "Pay-As-You-Go" plan. This will be the plan for the bulk of what we discuss here.

### Fixed Cost Pricing

Fixed cost pricing is what you would most likely be familiar with if you're new to cloud billing. You pay an agreed upon rate, and you get a resource for a set amount of time. This cost does not flex depending on usage like Consumption Based pricing does. In Azure, these are commonly referred to as "Reserved Instances". Essentially, you agree to pay for 1-3 years of using a VM, and are then given a "Reserved" rate that can be pre-paid up front, or paid monthly. Microsoft gets assurance of the resource being provisioned and generating revenue for a set length of time, and the customer gets a discount in exchange for this agreement. This pricing model is often utilized for Virtual Machines that will be required to run 24/7 anyways, thus making a fixed cost model more enticing, among other scenarios.

### Examples

#### VM - Consumption Based

We have a requirement for a virtual machine that is used to run an employee timecard software. This is a fairly lightweight application, and only needs to run on weekdays from 8AM to 6PM as hourly employees are not scheduled outside of this time range. In this instance, a consumption based VM would likely be our best option. Across four weeks in a month, with the VM being active for 10 hours 5 days per week, that's 200 hours per month. On a $0.188/hr consumption based VM, that's about **$22/mo**.

Compare that to a pre-paid reserved instance. For one-year reserved VM of the same size, due to it provisioned 24/7, the cost would be about $41/mo. Even with 3 years reserved, that still comes out to $36/mo. In this instance, consumption-based pricing is the most cost-effective method.

#### VM - Fixed Cost Reserved Instance

Let's take the above example and say now we need to have a VM that hosts a public database of their products. Our customer has specified that this resource must always be available, 24/7, as their customers cannot see what SKUs they have availble without it. In this case, we'd want to consider a reserved VM. At the same size that costs $0.188/hr, to run this for 730 hours (730 hours \* 12 months = 8760 hours in a year. 8760 hours / 24 hours = 365 days in a year) per month, that balloons our cost to **$70/mo.**

Let's now look at a reserved instance. For 1 year, we'd pay $495.55 upfront, or $41.33/mo. If we were confident in this resource being required for a 3-year term, that would further take our upfront cost to $958.96, further reducing the monthly cost to just **$26.64/mo.** In this instance, fixed cost reserved based pricing is the most cost-effective method.

## Controlling Costs

### Creating a Budget and Cost Alerts

Assigning a Budget to an Azure Subscription or Resource Group helps plan for costs and allows you to review expected vs actual spending. Additionally, you can use budgets to create spending alerts and kick-off automations that help control costs.

Start out in the Azure portal, in the scope you'd like the budget assigned to. For example, we want to place a budget on our Lab resource group, so we'll navigate there and select Budgets in the left navigation bar:

![](/files/NILRJMmiCs8iiqG2Zfsm)

In Budgets, we'll select the Add button. We can also change our scope to the parent Subscription here, if we'd like

![](/files/uog0MJ8k7b16rQavwLXt)

We'll then be shown the budget creation screen. Here we can set the name of the budget, the reset period, and any start and end dates to be defined. By default, budgets will be valid for 2 years. Helpfully, a total scope cost is shown in the right side of this page to help us define what the budget limit should be. As this is a lab group costs can fluctuate depending on what is being tested, so I've set this to a $100.

<figure><img src="/files/XWhd1kbNg5YbUBFVa6qt" alt=""><figcaption><p>Azure Budget Creation</p></figcaption></figure>

We'll now set our alert conditions on the next page. I've configured this to send me an email once this resource group gets to 80% of actual costs on the budget, or $80. Don't worry about setting an action group for now. I've then entered my email and saved the new budget and alerts.

<figure><img src="/files/PqPXuMNdov4DYNjj2zeE" alt="" width="484"><figcaption></figcaption></figure>

### Automating Cost Saving Actions

With Azure Automation Runbooks, we can take this a step further. [Check out this article on automating cost management actions to give yourself an extra set of eyes when controlling spending!](/technology/cloud-concepts/microsoft-azure/cost-management/automating-azure-cost-management)


# Automating Azure Cost Management

Who's going to be watching your budget all day? Azure is!

We're not perfect. Sometimes a resource that should be shut off doesn't get shut off, and you come back on Monday to see your budget is exceeded due to this. There's many different things you can do when a budget is exceeded, but for this demo we'll be taking the action of shutting down all VMs in a resource group. In a production environment, care should be taken to define what actions are acceptable to take. However, as this resource group is a non-production lab, we can assume it is safe to simply shut down all VM resources in the event our budget alert is triggered.

## Creating a Runbook

In our Resource Group, we'll start by creating an Azure Automation resource to house our Runbook. This will be used to define the set of actions to take when our budget is exceeded. Click the Create button on the Resource Group overview

![](/files/NoFI0aBHhJp1CqL2HNDX)

In the Marketplace, we'll search for and create the "Automation" resource

![](/files/p08aCzsB6JaLeptMN1Zx)

On the Basics tab, we'll set Subscription, Resource Group, Name, and Region Info. As this Automation Account can be used for many runbooks, we'll give it a more generic name of "LabAutomation". We can then proceed straight to the Review + Create tab and create the resource

![](/files/OjDJi6WVx2tgTeyhEnli)

Once the deployment finishes, we'll proceed to the Runbooks tab under Process Automation in the newly created Automation Account resource

![](/files/jtn4CsiP33aMeb5Vbkb4)

We'll use an existing runbook, so select the option to Browse Gallery

<figure><img src="/files/WmZebmUEo3scvs44nTc5" alt=""><figcaption></figcaption></figure>

Search for and Select the Stop Azure V2 VMs Runbook

![](/files/UhOrTdeewqQCNatRmdrl)

On the Import page, name and import the Runbook

<figure><img src="/files/oPjCfFTRt0PARINDMW3B" alt=""><figcaption></figcaption></figure>

You'll then be taken to a graphical editor. Publish the Runbook

![](/files/fmtZ1lNYo1iug6YG0IT6)

## Creating an Action Group

Next, we need to define a group of actions to take when a budget alert is triggered. To create this, we'll start by going back to our Resource Group to the Alerts tab in the left navigation bar

![](/files/xkBynIoSAWNumk6pOSOg)

Then click the Create button, and select Action Group

![](/files/j5sCL7LlShdRqNGHG5IL)

On the Basics tab, we'll define our Subscription, Resource Group, Region, and Name

<figure><img src="/files/1iCHHAaf9c4MWdRoVbjc" alt=""><figcaption></figcaption></figure>

Proceed to the actions tab. Here we'll link our Automation Account and runbook to this action group. Start by setting the action type to Automation Runbook

![](/files/aBFtIojlnrQFLsiq47Xe)

In the Configure Runbook flyout, set the Runbook Source to User, then select the subscription, Automation Account, and Runbook you created. Select Configure Parameters.

<img src="/files/IX3sNnhvt8FaWpu9N5VC" alt="" data-size="original">

{% hint style="info" %}
You may get an Unsaved Changes pop-up when clicking Configure Parameters. This seems to be a bug, and you can proceed anyways
{% endhint %}

Set the RESOURCEGROUPNAME parameter to the name of your resource group, click OK on the Parameters, then click OK on the Configure Runbook flyout. Name the action and create the Action Group

![](/files/srV6DUL9k4cuHOmK94f2)

## Assigning Actions to our Budget

Lastly, we need to tie it all together. Let's tell our budget about this new action group so it can fire it off when the conditions are met. Return to the budget you created in the Resource Group's Budget tab

<figure><img src="/files/LZq7LhvHUw32N0uREAgW" alt=""><figcaption></figcaption></figure>

Edit the Budget

![](/files/CRWfrhoOo1pzVoQYVnZ4)

In the Set Actions tab, we can now define our Action Group as part of our 80% used threshold, or create a new condition

![](/files/OrCCUsq8N6TdqOsZqm9Z)

And that's it! By following this guide, you've defined an automation runbook to shut down VMs, added it to an Action Group, and assigned that Action Group to your Budget Actions to ensure costly resources are shut down if you're about to exceed your desired spending.&#x20;


# Bot Framework

I, for one, welcome our new robot overlords

Ever dream about making a custom conversational bot? Read on to learn all about Azure Bot Framework Services


# Intro to Bot Framework

Hello, human...

## What is Bot Framework and Bot Service?

The Microsoft Bot Framework, which includes Azure Bot Services, is a library of building blocks to support creation of conversational bots. The concept of a Microsoft Teams helper app utilizing Adaptive Cards can be easily accomplished with Webhooks, however, Bot Framwork enables you to create a more natural interaction between your application and its users. The Bot Framework includes an SDK for C# and JavaScript (Java and Python SDKs are retiring 11/2023), and a RESTful API. For the first few documents, we'll be focusing on the REST APIs.

## What do I need to make a simple Teams Bot?

Bots contain a few major elements. From the start, an "Activity" is sent from your selected channels. This activity includes where the interaction came from, what happened, who sent it, and other conversational details. For example, a chat from Teams would include the user's display name and AzureAD Object ID, the conversation ID and message ID to reply to, and the contents of the message. These activities are then sent from the Bot Service to your Messaging Endpoint. This endpoint is a web app that is able to intake these activities, interpret them, take actions, and send a reply back. This can be created with the Bot Framework SDK, or any other automation platform that can support standard RESTful JSON API calls.&#x20;

## What methods will be shown to make a bot?

As this blog is mainly geared towards the MSP industry, I will be making these guides somewhat generic to what tool you would like to use. As long as it can interact with a REST API, it can work with bot service.

I personally utilize the RPA platform Rewst, so will make a separate post on how to set up a Rewst workflow as the messaging endpoint. This, however, is not required to be used.


# Creating your Bot Service

Some housekeeping before the fun stuff...

## Creating a Bot Service resource

Before we can start working on the bot logic itself, we need to make the Bot Service that will support the rest of this project. In your Microsoft Azure portal, head to the resource group you want the resource to be created in. Then, click the Create button.

![](/files/TIDr1bF4WptYZ1qbXvgi)

In the Marketplace, search for Bot and create a new Azure Bot resource

{% hint style="info" %}
Tip: Check the "Azure Services Only" box to easier find Azure resources
{% endhint %}

<figure><img src="/files/3NLyHaq0l5a2LWOwSw5p" alt=""><figcaption></figcaption></figure>

On the Basics tab, create a handle for your bot. This is a unique ID for your bot, and while you can change its display name later, you cannot change the handle. Set the subscription, resource group, and data residency. Select the free plan under Pricing.

<figure><img src="/files/PrUSVMeaZFpSd9fLU6Z3" alt=""><figcaption></figcaption></figure>

### Types of App Identities

Choosing the correct App Type under Microsoft App ID is crucial. The different types are:

* Single Tenant Identity
  * For Bots that will only be accessed from your tenant and will use your tenant for authentication. This is only supported by the C# and JavaScript SDK.&#x20;
  * Authentication is handled with Client ID and Secret
* Multi Tenant Identity
  * For bots that will be accessed from multiple Microsoft tenants and will use the bot service for authentication. Supported by all SDKs.
  * Authentication is handled with Client ID and Secret
* User Assigned Managed Identity
  * For bots that will be accessed from multiple Microsoft tenants and will use the bot service for authentication. Supported by only the C# and JavaScript SDK.
  * Authentication is handled by an Azure Managed Identity, and cannot be used with Client ID and Secret

Given these behaviors, we'll choose Multi-Tenant as we want a Client ID and Secret to be used by our RPA platform. If we were making a bot that was hosted on an Azure App Service, Managed Identity would be recommended.

<figure><img src="/files/XyqpItVTXcyxawEUOaoO" alt=""><figcaption></figcaption></figure>

You can now proceed to assign any tags you may want, and create the resource

## Configuring the Bot Service

#### Customizing your bot

Once your resource is created, navigate to it. Select Bot Profile under Settings in the left navigation bar

![](/files/5vpGHzr9uTDq5ZNTixbm)

On the Profile page you can set your bot's icon, name, and description

<figure><img src="/files/gcB5QbzLFBYTEkbhjV3m" alt=""><figcaption><p>Icon courtesy of DALL-E :)</p></figcaption></figure>

#### Connecting your bot to Teams

Click the Channels button in the left navigation bar

![](/files/B9DjnmuYoM7YZBGaoZRP)

Select the Microsoft Teams channel

<figure><img src="/files/oLNhNW4Qbs1FQkdbfW0o" alt=""><figcaption></figcaption></figure>

Accept the terms and click the Apply button to choose the Microsoft Teams Commercial channel. You'll now see Teams under your connected channels with a Healthy icon

<figure><img src="/files/JVGqw4lMmnqUP4TzsNLz" alt=""><figcaption></figcaption></figure>

## Getting your bot's Authentication

Your bot will happily send requests to you all day, but when responding you need to specify who you are back. By selecting Multi-Tenant on our app type, a new App Registration has been created in our Azure Active Directory tenant. Head on over to the Entra portal -> Azure AD -> Applications -> App Registrations

![](/files/CznCQeLdas05Rc5xnXib)

Under Owned Applications, select your bot

<figure><img src="/files/B9enRMY5eGlU48BFYbyr" alt=""><figcaption></figcaption></figure>

In the Essentials tab on the Overview page, note the Application (client) ID, and Directory (tenant) ID

![](/files/zLF04vGFrzj1AwEobQ4g)

Go to Certificates & Secrets, then create a new Client Secret. Name this secret and set its validity length

<figure><img src="/files/YL0YXPdnGZZu8wn3cQCe" alt=""><figcaption></figcaption></figure>

Copy the Secret Value to your clipboard before leaving the page! This secret will not be shown again, so document it somewhere safe

<figure><img src="/files/0O9iSGcf3pcYCbzcb9E8" alt=""><figcaption></figcaption></figure>


# Interacting with your Bot Service's API

Who needs an SDK?

With the configuration done, we can now test out our Bot Service. We'll need to connect a messaging endpoint to receive the messages and send a response. For the sake of testing, we'll use the awesome tool <https://webhook.site>. This will allow us to generate a webhook and capture any data sent to it for inspection. Copy your custom webhook URL

<figure><img src="/files/uPMf70Hb4P2HHO7Y03iY" alt=""><figcaption></figcaption></figure>

Go back to your Bot Service and click on Configuration in the left navigation bar. Enter your webhook URL into the Messaging Endpoint field and apply the settings

<figure><img src="/files/j1rlYoV70l4pLKYOVj9v" alt=""><figcaption></figcaption></figure>

Go back to the Channels tab in the left navigation bar, and click "Open in Teams" under Actions for your Teams channel connector

<figure><img src="/files/o0RiCRp7OQvBfFr5jJFY" alt=""><figcaption></figcaption></figure>

Teams will now open a conversation with your bot! Send it a message so it can send data to Webhook.Site

<figure><img src="/files/REV7rf45y0OccgdhQjGn" alt=""><figcaption></figcaption></figure>

## Receiving messages

Back in our Webhook.Site tab after we sent something to our bot chat, you'll find data that was sent as a POST to our webhook. This includes information on what the message says, where it came from, who sent it, and how to reply to it

<figure><img src="/files/OHC3oVH86AL95o18oRNp" alt=""><figcaption></figcaption></figure>

## Responding to Messages

To respond to our message, we'll need to send a POST back to the bot service. There's a variety of apps that can help with this, the most common being Insomnia and Postman. I'll be using Insomnia, but the steps on other API tools should be similar. I'll also include `curl` commands for those who prefer a command line.

Before we start sending requests, we need to collect some data from the POST to our webhook:

* `serviceUrl`
* `conversation.id`
* `recipient.id`
* `recipient.name`

### Getting Authenticated

We'll start by getting our authorization token to respond to this message. With your Client ID and Secret, send the following request

```http
POST https://login.microsoftonline.com/botframework.com/oauth2/v2.0/token
Host: login.microsoftonline.com
Content-Type: application/x-www-form-urlencoded

grant_type=client_credentials&client_id=MICROSOFT-APP-ID&client_secret=MICROSOFT-APP-PASSWORD&scope=https%3A%2F%2Fapi.botframework.com%2F.default// Some code
```

In Insomnia, it looks like this:

<figure><img src="/files/Qv6dhAEdkCvLzGZxn5sa" alt=""><figcaption></figcaption></figure>

To send in `curl`, format it as:

```bash
curl --data-urlencode "grant_type=client_credentials" \
--data-urlencode "client_id={clientID}" \
--data-urlencode "client_secret={clientSecret}" \
--data-urlencode "scope=https://api.botframework.com/.default" \
https://login.microsoftonline.com/botframework.com/oauth2/v2.0/token
```

When sent, the response should look like this:

```json
{
	"token_type": "Bearer",
	"expires_in": 86399,
	"ext_expires_in": 86399,
	"access_token": "eyJ0eXAiOiJKV1.....ALotMoreCharacters"
}
```

Note the `access_token` value for later use. We now have all the information we need to send our response!

### Sending a Reply

First, we need to know where to send the reply to. Since Microsoft has several regions for Teams, the message to our endpoint with the message details also contains a `serviceUrl`. This will be formed to make the base URL. The base URL will be the service URL, plus `/v3/`, plus the API endpoint.&#x20;

The endpoint we need to reply to a chat message is `conversations/{conversationId}/activities`. Therefore, with the `serviceURL` of `https://smba.trafficmanager.net/amer/`, our reply URL should be `https://smba.trafficmanager.net/amer/v3/conversations/{conversationId}/activities`

The body for the reply to a chat message should be in JSON and at minimum contain:

```json
{
    "type": "message",
    "from": {
        "id": "BotID",
        "name": "bot's name"
    },
    "conversation": {
        "id": "abcd1234"
    },
   "recipient": {
        "id": "UserID",
        "name": "user's name"
    },
    "text": "Response to human!"
}
```

Putting this all together, in our case this would look like the following in Insomnia

<figure><img src="/files/lN3wLilc9AWZtMmJCk6W" alt=""><figcaption></figcaption></figure>

In `curl`, this would be:

```
curl --request POST \
  --url https://smba.trafficmanager.net/amer/v3/conversations/{conversationId}/activities \
  --header 'Authorization: Bearer {Access_Token from prior step}' \
  --header 'Content-Type: application/json' \
  --data '{
    "type": "message",
    "from": {
        "id": "BotID",
        "name": "BotName"
    },
    "conversation": {
        "id": "ConversationId"
    },
   "recipient": {
        "id": "UserID",
        "name": "UserName"
    },
    "text": "Hello, Human!"
}'
```

When successfully sent, you'll get a `201` success code for the request and the response body of an ID for your message

<figure><img src="/files/y7z640PAbWuPmJsGucJA" alt=""><figcaption></figcaption></figure>

Your message is then sent to the user!

<figure><img src="/files/9cmaELHKyWIIKUKGv7pP" alt=""><figcaption></figcaption></figure>


# Sending Proactive Messages

Everyone could use some more messages!

So far we've learned the basics of how the Azure Bot Service works, how to create our own bot and associated resources, how to receive messages, and then how to reply to them. But what if we want to send a message to a user before they message us first? With a Conversation ID and User ID we can!

### What you'll need

* An Azure Bot integrated to Teams
  * Haven't been following along? [Start here!](/technology/cloud-concepts/microsoft-azure/bot-framework/intro-to-bot-framework)
* An App Registration for your bot
  * You'll also need the Client ID and Secret from this to request access tokens
* Some sort of database (OPTIONAL)
  * We'll go into this a bit further later on, but some attributes of the conversation with a user, such as its ID, generally won't change once they've been assigned. It is much quicker and more efficient to query these from a database.

### Getting conversation and user IDs

Before you can send a message, you need to know where to send it to. The user part of this is easy, this can be just a user's Entra Identity User Object ID (aka AAD Object ID). However, we also need to have a valid conversation in teams to send it to. Lucky for us, one is created automatically in a few scenarios:

* A user installs your bot as an app
  * We'll cover this later, so don't worry too much about it now
* A user messages your bot
* A user starts a new chat with your bot
* A user adds your bot to an existing conversation

In this guide, we'll mainly be focusing on conversation IDs from prior messages and new chat sessions. In the next post, we'll review how to package your bot as an installable native Teams app for the first option.

So, let's get some IDs! Here's a trimmed down version of what you get when someone messages your bot:

```json
{
  "id": "1693284ccccc",
  "from": {
    "id": "29:1mZd6lzxxxxx",
    "name": "Brandon Martinez",
    "aadObjectId": "xxxx"
  },
  "text": "Hello!",
  "type": "message",
  "channelId": "msteams",
  "conversation": {
    "id": "a:xxxxxxxx",
    "tenantId": "xxxxx",
    "conversationType": "personal"
  }
}
```

From top to bottom, we have an ID to reference back to for the message itself, from information including the name and AAD Object ID of the user, and the conversation ID. Perfect! I'll set my RPA platform to go ahead and save these values in a database table, but if you're following along without one, you can just note these down.

{% hint style="info" %}
The from ID and conversation ID are different things despite being in the same format. We want just the conversation ID. Be sure not to mix them up!
{% endhint %}

What about when someone starts a new chat or adds your bot? You'll get something that looks like this:

```
{
  "id": "",
  "type": "conversationUpdate",
  "channelId": "msteams",
  "conversation": {
    "id": "a:xxxxx",
    "tenantId": "xxxxx",
    "conversationType": "personal"
  },
  "membersAdded": [
    {
      "id": "29:xxxxx",
      "aadObjectId": "xxxxx"
    },
    {
      "id": "xxxxx"
    }
  ]
}
```

We can see the same data is available here, but structured slightly differently. Note how the `membersAdded` property is a list of two objects, one containing an `aadObjectId` and the other with just a generic ID. Since technically both the user and your bot were added to the same conversation, they both appear in the list of added members. Your bot won't have an `aadObjectId` attribute, so it's easy to distinguish which one is your user. The `id` value in the object without it will also match your bot's ID.&#x20;

{% hint style="info" %}
Curious about how it works when the bot is added as an app? We'll come back to that in the next post
{% endhint %}

### Sending the message

{% hint style="info" %}
We'll can use the same Access\_Token we've used when replying to a message, provided it's not expired. If it is, the same request can be resent to generate a new one. Not sure how to get an access token? Check out this prior post: [Interacting with your Bot Service's API](/technology/cloud-concepts/microsoft-azure/bot-framework/interacting-with-your-bot-services-api#getting-authenticated)
{% endhint %}

Now that we have the information we need, we can proceed to actually sending the message. Believe it or not, this is the easiest part. Why? You've already done it! Helpfully, the same JSON schema is used for replying and sending proactive messages. This should look familiar:

```sh
curl --request POST \
  --url https://smba.trafficmanager.net/amer/v3/conversations/{conversationId}/activities \
  --header 'Authorization: Bearer {Access_Token from prior step}' \
  --header 'Content-Type: application/json' \
  --data '{
    "type": "message",
    "from": {
        "id": "BotID",
        "name": "BotName"
    },
    "conversation": {
        "id": "ConversationId"
    },
   "recipient": {
        "id": "UserID",
        "name": "UserName"
    },
    "text": "Hello, Human!"
}'
```

And, when you've successfully sent the request, you'll get an ID back just the same:

```json
{
	"id": "1693680152973"
}
```


# Packaging Your Bot

🤖+📦

### Why deploy as an app?

Deploying your bot as an app has several advatages, including allowing it to be pinned to a user's app list for quick access, show suggestions for actions it can tage, and provide a method to collect ConversationIds for an entire organization, among other things.&#x20;

### Creating an App Package

To create an app package, you need three things. A color logo, an outline logo, and a manifest JSON file. Let's start with the icons, as they do need to be in a consistent format. To make things easy, go ahead and create a new directory now to hold all these items.

#### The Color Icon

This is fairly straight forward and is the primary icon for your bot people see when they message it. We'll need this image to meet the following requirements:

* Image must be 192x192px with background
* Background must be solid or transparent
* Icon within image must reside in a "safe region" of the center 96x96px

Visually, this is the guide provided by Microsoft

<figure><img src="/files/dWCPryS1MlsNZa5ade9Z" alt=""><figcaption><p>Credit to Microsoft for image</p></figcaption></figure>

&#x20;We'll save this image in our directory as `color.png`

#### The Outline Icon

This one is a bit trickier, and represents the outline shown when the app is pinned to the sidebar of teams. As these icons are monochromatic and color matched to the current theme in the frontend, our outline icon must also be either white with a transparent background, or transparent with a white background. No other colors will work, it may accept the icon but end up looking exceptionally janky in the Teams client. This icon simply needs to be 32x32px, as there is no background.

<figure><img src="/files/BEZ8AQ9vNj3pRB3EaxoF" alt=""><figcaption><p>Credit to Microsoft for image</p></figcaption></figure>

For more information on the icon formatting, [reference this Microsoft document](https://learn.microsoft.com/en-us/microsoftteams/platform/concepts/build-and-test/apps-package#app-icons). Save this outline icon as `outline.png`

#### The JSON Manifest

This is the fun part where we actually glue everything together. We'll start with the basic schema that contains all the required bits for our usage:

```json
{
    "$schema": "https://developer.microsoft.com/json-schemas/teams/v1.16/MicrosoftTeams.schema.json",
    "manifestVersion": "1.16",
    "version": "1.0.0",
    "id": "%MICROSOFT-APP-ID%",
    "developer": {
        "name": "Publisher Name",
        "websiteUrl": "https://example.com/",
        "privacyUrl": "https://example.com/privacy",
        "termsOfUseUrl": "https://example.com/app-tos"
    },
    "name": {
        "short": "Name of your app (<=30 chars)",
        "full": "Full name of app, if longer than 30 characters (<=100 chars)"
    },
    "description": {
        "short": "Short description of your app (<= 80 chars)",
        "full": "Full description of your app (<= 4000 chars)"
    },
    "icons": {
        "outline": "A relative path to a transparent .png icon — 32px X 32px",
        "color": "A relative path to a full color .png icon — 192px X 192px"
    },
    "accentColor": "A valid HTML color code.",
    "bots": [
        {
            "botId": "%MICROSOFT-APP-ID-REGISTERED-WITH-BOT-FRAMEWORK%",
            "scopes": [
                "team",
                "personal",
                "groupChat"
            ],
            "commandLists": [
                {
                    "scopes": [
                        "personal",
                        "groupChat"
                    ],
                    "commands": [
                        {
                            "title": "Personal command 1",
                            "description": "Description of Personal command 1"
                        },
                        {
                            "title": "Personal command N",
                            "description": "Description of Personal command N"
                        }
                    ]
                }
            ]
        }
    ]
}
```

Looks like a lot, but let's break it down. These are the more backend properties you don't generally need to worry about:\
`$schema` is a URL reference to the JSON schema used.\
`manifestVersion` is the version of app manifest schema users\
`version` is the version of your app, so 1.0.0 for the initial release

{% hint style="info" %}
Not sure how to number your app versions? Check out [Semantic Versioning](https://semver.org/)
{% endhint %}

Next, we have the bits we need to change:

`id` is your bot's ID as it shows in the Bot Framework. This can be found in the Configuration tab of your Azure Bot under "Microsoft App ID"\
`developer.name` is the name you wish to publish the app under, such as `GigaCode`\
`developer.websiteUrl`,  `developer.privacyUrl`, and `developer.termsOfUseUrl` are fairly self explanitory, and should point to your domain.\
`name.short` is a maximum 30 character short name for your app\
`name.full` is a maximum 100 character full name for your app, and must be different than the short name\
`description.short` is a maximum 80 character description of your app\
`description.full` is a maximum 4000 character description of your app\
`icons.outline` is the relative path to your outline icon. Assuming it is stored in the same directory as the manifest, this can be set to `outline.png`\
`icons.color` is the relative path to your color icon. Assuming it is stored in the same directory as the manifest, this can be set to `color.png`\
`accentColor` is any valid HTML color code to use as a background for icons

Finally, we have the fun part where we can customize even further:

`bots.botId` is the same id defined earlier from the bot framework\
`bots.scopes` defines where your bot can be called from: A `team`, `groupChat`, or `personal` conversation

`bots.commandLists` is where we can define the action "hints" to provide to users when they click the chat box:

`bots.commandLists.scopes` defines the scope of where these commands will be shown, using the same `team`, `groupChat`, or `personal` conversation types\
`bots.commandLists.commands` is where we can start defining objects for our suggestions. `title` will be the most visible part shown to users, and what is entered into the chatbox when clicked. `description` is a smaller area of text below the title to provide the user with further context on the item selected

Want different commands for different scopes? Just make a new object containing the above data in the `bot.commandLists` array!

Once you're done, save this file as `manifest.json` in the same directory as the icons

#### Packing it all up

Now that we have our icons and manifest ready, and they're all in the same directory, we can turn this into a format Teams can recognize. Which is.... a simple ZIP file!

Yup, that's it. Right-click and "Compress to ZIP file" is all you need to do:\
![](/files/s5PvsYwmsAYyzDRst8hc)

### Importing to Teams

Importing is just as easy if you are just adding for yourself. Go to the Apps tab in your Teams client as a Teams Admin, click "Manage your apps", then "Upload an app" and select the zip file you made

<figure><img src="/files/23zEROaxjhCYR6hRxfl2" alt=""><figcaption></figcaption></figure>

You will then see an option to install and open the app:<br>

<figure><img src="/files/gdIb7CeQuxDvDYxxV6eU" alt=""><figcaption></figcaption></figure>

### Bulk Deploying the App

Individual installs are alright for testing, but what if we want to deploy this to all our internal users at once (or to a client)? We can utilize Teams policies to accomplish this goal.

First, head to the Teams Admin center at <https://admin.teams.microsoft.com/>. Select Teams apps, then Manage apps in the navbar\
![](/files/tw8Im1u2b0DayIESIepb)

If you've already uploaded the app as an admin, it should be in this list. Otherwise, click the "Upload new app" button and upload the ZIP package

<figure><img src="/files/OF7fuBsUd1dfQ3yHUg7P" alt=""><figcaption></figcaption></figure>

Let's click on our bot and take a look at the options real quick before moving on. Note the Upload File button under New version. This is where you can provide new packages with updated commands or logos

<figure><img src="/files/xZrtEWFU0if8qPPdeUTE" alt=""><figcaption></figcaption></figure>

Now that we have our app uploaded to our organization, let's make a policy to install it. Head over to the Setup policies tab under Teams Apps\
![](/files/GwPpmpjj2GFnMjtZ5BO3)

We'll click the Add button to add a new policy\
![](/files/rkEX5G0G8LqanR5tkCaw)

Give the policy a name and description. Optionally, you can also control the settings here to allow users to upload and pin their own custom apps. By default, standard users cannot upload their own apps.

<figure><img src="/files/J69NLrHOvForqx90AVIG" alt=""><figcaption></figcaption></figure>

Under Installed apps, click Add apps and select your bot

<figure><img src="/files/DibYMiGR3gpegk7nozzS" alt=""><figcaption></figcaption></figure>

The list of installed apps should show our bot and its ID once selected. Optionally, you can pick any other apps you'd also like to install on behalf of users here.

<figure><img src="/files/9uUgnAAfM8nIVf9vYS6z" alt=""><figcaption></figcaption></figure>

In the Pinned apps section, we can define what should show in the left navigation bar in the teams client and its order for our users. You can add your bot here to make it highly visible and drive further adoption<br>

<figure><img src="/files/Wu8GKt7Ct3RraKT2WgxO" alt=""><figcaption></figcaption></figure>

We can now save our policy. We now have to assign users to our app policy, and we have two ways to accomplish this. First, by highlighting our policy and selecting Manage users, then add users to assign individual accounts<br>

<figure><img src="/files/0bOD1FyLUCns7WEinIqe" alt=""><figcaption></figcaption></figure>

To bulk assign users by groups, go to the Group policy assignment tab above the policy listings. No, this does not refer to Active Directory Group Policy despite the name\
![](/files/NiAkYAvEiQTOEjklFjJa)

Click add, then select a group. We'll use "All Users" to make things easier. We can then select our policy and set the rank. The rank number specifies which policy should be inherited for members of multiple groups - the higher the rank the more precedence it takes<br>

<figure><img src="/files/4RshigbqoZA3HFSOPoUe" alt=""><figcaption></figcaption></figure>

And that's about it! After the required 30-90 "Microsoft Minutes" the app should start to appear in users Teams clients, and you will start to receive POSTs to your messaging endpoint containing conversation IDs webhook for `installationUpdates`


# Zero Trust Networking


# What is Zero Trust

Threat actors are getting smarter by the day, and the assumptions around protecting your network and assets are constantly evolving. Zero Trust changes the thinking from premise based "Protect the castle perimeter" thinking to a principal of "Never trust, always verify"

## Why is this important?

With more and more systems becoming separated from a single site and moving to cloud hosting, the "network perimiter" is no longer a viable entity to protect. Additionally, threat actors are targeting the usage of misconfigured corportate networks to move laterally within networks.

Key principals of Zero Trust include:

1. Verify Explicitly: Always authenticate based on *all* available datapoints, including identity, asset, network, and other signals
2. Enforce Least Privilege: Chances are, your call center agent doesn't need direct access to the production software environment. Limit your user and application access to only what is nessesary based on who is accessing it, but also on factors like the device security they are connecting in from
3. Assume Breach: Operate your environments with the assumtion the threat actors are already there. Segment access, apply monitoring, and respond quickly to alerts

## How is Zero Trust different from other networking theories?

### Traditional remote site networking (Site to Site, Point to Site)

* One network is created for resource access, segmented based on the incoming connection parameters
* VLANs can be used to segment networks and stateful firewalls used to control which applications/ports can be used to cross between VLANs
* Typically, only a valid identity is required to access the network
* Prone to misconfigurations allowing for a wider scope of lateral movement between networks with high confidentiality and integrity requirements

In my experience working in the IT and security consulting world, many networks still operate under these practices. Does this mean they are inherently unsafe? Not necessarily. However, they may not be as robust as they can be, and can offer much higher attack surfaces for movement between networks. It's not uncommon for an initial attack vector to be a VPN client with stolen credentials, whether from an internal employee or contractor, with way too much access scopes. When everyone has access to a whole network subnet, it makes it much easier to expand the scope of what you can see

### Zero Trust Networking

* Eliminates the concept of a trusted internal network and untrusted external network
* Continuously verifies each access attempt, whether from inside or outside the network
* Evaluates each resource to access individually, rather than allowing full access to a subnet of resources
* Prioritizes secure access to resources based on modern authentication protocols

By contrast, Zero Trust networking essentially makes a VLAN for each individual resource. It's a bit more than that, but assume that each resource is on its own network essentially for access when configured properly. If someone needs to get into the network to access a file share resource, this does not also automatically give them access to an application server, or to an intranet web resource. Each must have their access evaluated individually, and you may be authenticated to connect to all but not allowed to access some based on other signals

## Scalability Benefits

Modern Zero Trust networking platforms allow you to quickly and security create individual tunnels to resources, create many access rules, and modify/revoke access if any changes need to be made. Where on a traditional network you may need to add a resource to an overscoped subnet or spin up multiple stateful rules, VLANs, and NAT policies, Zero Trust allows you a high level of adaptability and access to resources no matter where they are on the web.

## Chosing your Zero Trust client

There's many platforms to choose from, with some big players like Cisco, Microsoft, Cloudflare, and Zscaler. We decided to go with Cloudflare, for the following reasons:

* Maturity
  * When evaluating, at the time it was a much more mature platform that over offerings providing aribtrary TCP tunneling, browser based isolation and rendering, and the ability to insert short life SSH certificates for SSH connections
* Affordability
  * Working in the SMB (Small/Medium Business) space, afforability is an important consideration. Cloudflare's offerings are relatively easy to include in our existing support plans, including a free plan
  * While a free plan is available, it does have limitations for log retention, network locations, and user counts. Additionally, it does not have an uptime SLA and only community forum support
  * We typically deploy the standard "Pay-As-You-Go" plan, which at the time of writing, is $7/user/mo. This includes 100% uptime SLA guarantees, support with a median response of 4 hours, up to 50 network locations, and 30 days of dashboard/log retention
* Reliability
  * Cloudflare serves 20% of all internet traffic globally, and has a global edge network that is <50ms from 95% of all internet users. While outages happen, they have a track record of reliable services, and include a 100% uptime SLA in their paid plans
* Commonality
  * Already using Cloudflare for our DNS and WAF management, adding their ZTNA/SASE platforms were more appealing than adding yet another vendor to our toolbelt

Does this mean other platforms are bad? No! This platform just works the best for us, but Microsoft Global Secure Access has come a long way since we evaluated it, and other enterprise platforms are spoken of highly by our peers. While this section will be using the setup of Cloudflare ZTNA as our example, the concepts should carry over into most other plaforms. I highly recommend you evaluate all options available to you to find the best solution for your organization, not just ours.


# Setting Up Your Tenant

As mentioned in [Welcome to Zero Trust](/technology/zero-trust-networking/what-is-zero-trust), we'll be proceeding with Cloudflare Zero Trust in these guides. However, the overall concepts should apply to most other Zero Trust Network platforms.

## Registering your accounts

To get started, you'll need to have a Cloudflare account. You can register one for free at <https://dash.cloudflare.com/sign-up>

Once you have your account, you'll see the Cloudflare dashboard. Head over to the Zero Trust tabs to proceed through the setup. The exact process may differ depending on the current state of the Cloudflare portal's UI and your region, but you'll be prompted to setup your Team Name and select a plan. Set the Team Name to something unique and relevant to your organization, but don't stress over it too much right now - you can always change it later

### Choosing a plan

For most people starting off, either as a personal lab tenant or in the evaluation phase for your organization, and free tier plan will be fine. The main difference you get between the free and Pay-As-You-Go plans is a 100% Uptime SLA, chat/email support, more network locations, and longer log retention. You will also be limited to a maximum of 50 users on the free tier plan. For the rest of this guide, I will be working with features available in the free plan unless stated otherwise

{% hint style="info" %}
You may be asked to enter card information at this stage. This is required for the free plan as a $0 purchase, but you will not be charged unless you upgrade
{% endhint %}

### Initial settings

Once you're in the portal, we'll start in the settings tab. We won't hit all the options here now, but here's some quick things we can update to customize the experience

### Account Settings

Not much to do here upfront, but you can change your plan and payment information if you choose to upgrade. One field to note is the User Seat Expiration. This can be used to automatically remove a license from a user if they have not signed in for a specified amount of time. I recommend, however, setting up SCIM provisioning to handle this later on in the guides

### Custom Pages

Here we can update our team name. This can be changed at any time, but must be unique. Additionally, you can configure custom block and login pages. I recommend branding these to your organization's standards to add additional trust in the platform to end users

### Network

**Gateway Logging**: The settings here can control the amount of logs sent and retained in the plaform. We'll leave this default for now, but you can choose to modify these settings if you'd like. Cloudflare collects a good amount of PII, which can be excludeed here. Cloudflare considers the following as PII:

* Source IP
* User email
* User ID
* Device ID
* URL
* Referer
* User agent

You can also enable Enhanced Logging here. This will read connection information from the HTTP body rather than the headers. Be mindful this will cause a performance hit, especially in file heavy contexts. As always, configure these options to align with your organization's specific policies.

**Firewall**: Here you can configure the Firewall settings for when you are running the WARP gateway agent on endpoints, or if you have a WARP connector configured on your private network's edge router.

* Proxy
  * Let's enable this setting first. This will ensure all traffic is proxied through the WARP agent, and network policies are applied. You can select TCP, UDP, and ICMP traffic. We'll just start with TCP

{% hint style="warning" %}
Proxying UDP traffic may have significant negative effcts on your network, especially with VOIP performance. Be sure you fully understand the impact of enabling any proxy options before doing so in a production environment
{% endhint %}

* TLS decryption
  * With most traffic being HTTPS, you will likely be limited in the amount of information you can inspect. This option will allow you to scan HTTPS traffic by allowing a root CA certificate installed on the endpoint with WARP to decrypt HTTPS traffic

{% hint style="danger" %}
This is a *very* impactful setting, and is prone to breaking things. Many apps use certificate pinning, and will not function properly with TLS decryption enabled. Exclusions must be set for these applications. Before enabling this in a production environment, be sure you've tested all line of business apps to ensure compatibility. More information can be found [in this Cloudflare KB document, which includes a list of known incompatible applications.](https://developers.cloudflare.com/cloudflare-one/connections/connect-devices/warp/troubleshooting/common-issues/#tls-decryption-is-enabled-and-the-app-or-site-does-certificate-pinning)
{% endhint %}

* Match exteneded email addresses
  * Allow for extended email addresses, often being "plus addressing" formatted, to be used as criteria for firewall policies. For example, `username+somethingElse@domain.com`
* AV inspection
  * Enable the scanning of files through the Cloudflare Gateway. The following criteria is used to determine if a file should be scanned:
    * If the Content-Disposition HTTP header is Attachment
    * If the byte signature of the body of the request matches a signature identified as one of the following file type categories:
      * Executable (e.g., `.exe`, `.bat`, `.dll`, `.wasm`)
      * Documents (e.g., `.doc`, `.docx`, `.pdf`, `.ppt`, `.xls`)
      * Compressed (e.g., `.7z`, `.gz`, `.zip`, `.rar`)
    * If the file name in the Content-Disposition header contains a file extension that indicates it is one of the file type categories above

**Bypass decryption of Microsoft 365 Traffic** is used to create a default Do Not Inspect policy for known Microsoft 365 traffic. This optimizes performance and interoperability, and I reccomend enabling it;

With that, we've configured the initial tenant level settings! We'll cover the remaining settings pages in the following documents.


# The Human Side of Tech

It's not all about computers and automation

Technology is cool, right? But what's even cooler are the hard working folks who keep that technology running every day. Chances are, if you're reading this you fall into that group of hard working people.&#x20;

Unfortunately, the human aspect of this industry is often overlooked. My goal of this section is to be an open and honest collection of deep-dives into different mindsets and habits, and how to provide upkeep to your mental health.

When it comes to mental health in particular, if you know me you know I'm a very open book with that if someone wants to talk. I think normalizing open discussions about topics such as mental health that had been previously stigmatized is incredibly important.

{% hint style="danger" %}
Hotlines can provide free and fast access to help if you need support yourself or need help supporting a friend. If you're concerned about a friend, please encourage them to contact a hotline as well. Many now support SMS text messages if you prefer to message or otherwise cannot speak on the phone.

<https://findahelpline.com>

This site contains a list of global support hotlines, including multiple countries and types of help. There is a Quick Exit button provided if searching for help may cause further conflict. Services are verified and vetted by [ThoughtLine Ltd](https://www.throughlinecare.com) to ensure they are legitimate and operational.
{% endhint %}

While I am not a professional, I have been through my fair share of struggles, and believe being open and willing to talk to others can be a good resource as well. I can't promise that I'll make things better, but I can promise to listen and relate where possible.

If you have an immediate crisis, please use the resources linked above. But, if you just need someone to chat with, I've opened my DMs on Discord for the ***@gigacode*** account


# Your Mental Bandwidth

Are you overloading your brain?

## Understanding Mental Bandwidth

### Bandwidth?

If you're reading this, you probably understand the concept of "Bandwidth" when it comes to networking. The origination of the phrase is with analog radio, where frequency "bands" are a set of frequencies a specific signal can occupy. Therefore, a bandwidth is the difference between the lowest and highest frequency. Basically, how much you can cram into that specific radio band. For example, in the world of ham radio (73s to any hams reading 😀), a popular range of frequencies is the 70cm band which is 420Mhz to 450Mhz.

In modern usage, bandwidth represents the transfer rate or capacity of a given network. When you're getting 500Mbps of internet, you have up to that amount shared across your entire network to play with. If you have 5 devices all streaming 100Mbps, you've reached a limit. More devices can transfer data, but all devices will then need to slow their connections to remain within that 500Mbps bandwidth.

### Applying this to yourself

Just like a network, you can only handle so much information and activities at once. When you exceed your mental bandwidth, the quality of your performance declines. Just as a saturated network starts dropping packets or delivers them slowly, a mind overwhelmed by tasks might produce shoddy work, forget important details, or suffer from decision fatigue.

The goal of this post is to help you understand what can lead to overutilization of your own bandwidth, and ways to manage this.

***

## Mental Bandwidth Saturation = Burnout

Burnout is not just a bad day or a tough week; it's the result of a prolonged period of mental bandwidth saturation. Over time, this can manifest in lots of ways that impact both your personal and professional life.

**Emotional Effects**

1. **Detachment**: Just as a consistently overloaded network might begin to drop connections or become unresponsive, you may find yourself emotionally detached from work and relationships.
2. **Increased Irritability**: Constant saturation can increase your "latency" in emotional responses, making you quick to anger or frustration.
3. **Decreased Social Interaction**: Just as a saturated network might prevent new connections, you may find yourself avoiding social commitments, further isolating you and exacerbating feelings of loneliness and stress.

**Cognitive Impacts**

1. **Reduced Creativity**: When your mental bandwidth is saturated, there's little room for innovative or out-of-the-box thinking. You are essentially in a survival mode, aiming to keep the basic functions running, much like a network slowing down to maintain essential services.
2. **Decision Fatigue**: Making choices becomes increasingly taxing as your mental resources are depleted, and you might find yourself avoiding decisions altogether or making poor choices due to the lack of mental "processing power."
3. **Memory Issues**: As saturation escalates, your brain may start "dropping packets" in the form of forgetfulness or gaps in memory.

**Physical Health**

1. **Fatigue**: A system running at full capacity for too long is prone to wear and tear, and the human body is no different. You may experience persistent tiredness that doesn't go away with rest.
2. **Immune System**: Prolonged stress and mental saturation can weaken your immune system, making you more susceptible to illnesses, much like a network becoming more vulnerable to attacks when strained.

### Preventing Burnout - Your Early Warning System

Dealing with burnout tends to get more difficult as it progresses, and in my experience, it is much easier to be mindful of it and take steps to prevent it from sneaking up on you. Before you can take action, you need to recognize that you're approaching your mental bandwidth limit. Much like a network administrator uses monitoring tools, be attuned to signs of fatigue, stress, reduced productivity, and mood swings. These are your early warnings.

#### Capacity Planning: Know Your Limits

The first step in preventing network saturation is understanding the limits. Do the same for yourself:

1. **Take Stock of Your Responsibilities**: List out all your tasks, deadlines, and responsibilities. Estimate the time and emotional energy they require.
2. **Set Boundaries**: *Know when to say no. Overcommitting will only lead to saturation.*

#### Regular Maintenance: Take Breaks

Networks often need downtime for maintenance to perform optimally; you do too.

1. **The Pomodoro Technique**: Work in blocks of time (say, 25 minutes), then take a short 5-minute break. This can prevent mental fatigue and maintain high performance throughout the day.
2. **Physical Exercise**: Exercise is not just good for your body; it's great for your mind too. Even a 20-minute walk can reset your mental network.

#### Connection Pooling: Build a Support System

Don't carry the load all by yourself. Distribute tasks and share responsibilities to prevent overload.

1. **Delegate**: Pass on tasks that don't necessarily require your expertise.
2. **Communicate**: Regularly talk to friends, family, and mental health professionals to offload emotional and psychological stress.

#### Emergency Shutdown: Take Time Off

If all else fails, the most effective way to prevent burnout may be to temporarily disconnect, giving yourself time to reboot and recover.

***

## What's next?

Is prevention always possible? No! Sometimes you can take all the "right" steps and still run into challenges, and that's OK! The important part is realizing you've been knocked down and getting back up. I believe this needs its own post to fully address, so keep an eye out for future musings :smile:


# Managing Internal Expectations

Making sure you are kind to your time

In a world where hustle culture is glorified, and "more" is considered synonymous with "better," it's easy to find ourselves saturated, both mentally and emotionally. While the last post touched upon managing your mental bandwidth to avoid burnout, it's crucial to note that the source of this overload often isn't an external force—it's ourselves. Let's dive into how we can manage not just the expectations of those around us but also the demands we put on ourselves.

### The Source of the Saturation: It's Often Within

Before you point fingers at your boss, your partner, or your friends for the overflowing plate of responsibilities you're dealing with, take a moment to consider who said 'yes' to those tasks in the first place. Chances are, your biggest critic and taskmaster is looking back at you in the mirror every morning.

#### The Self-Imposed Load

Your ambition, perfectionism, or fear of missing out may be the culprits that push you to take on more than your mental bandwidth can handle, leading you closer to burnout.

#### The External Load

Simultaneously, social norms and expectations often push us toward an unsustainable pace of life. Whether it's the boss who always seems to need one more thing or the social circle that equates busyness with importance, external pressures are there—but it's up to us to say yes or no.

### Setting Boundaries: A Two-Way Street

Managing external expectations starts by setting boundaries, and that applies to the boundaries you set for yourself as well.

#### Internal Boundaries

1. **Self-Awareness**: The first step is understanding what you can realistically handle. Just as you wouldn't overload a network beyond its capacity, don't overload your day with an unrealistic list of tasks.
2. **Quality Over Quantity**: It's better to do fewer tasks well than many tasks poorly. Prioritize what truly matters.
3. **Self-Compassion**: Understand that it's okay not to be 'on' all the time. Give yourself the permission to relax and recharge.

#### External Boundaries

1. **Transparent Communication**: Be clear about what you can and cannot do. Honest communication helps set realistic expectations.
2. **The Power of No**: Saying 'no' is not a sign of weakness; it's a sign of knowing your limits.
3. **Delegate**: Pass tasks to others when possible, and don't be shy to ask for help when you need it.

### Practical Tips for Setting Boundaries

1. **The Prioritization List**: Make a list of all your tasks and commitments. Rank them based on urgency and importance. Anything that falls too low on both axes might be something you can say 'no' to.
2. **Time Blocking**: Allocate specific blocks of time for specific tasks, including time for yourself. Stick to it as much as possible.
3. **Check-ins**: Regularly check in with yourself and others to make sure expectations are aligned. This is your preventive maintenance to ensure you're not heading towards saturation.
4. **Create a Buffer**: Always keep some 'free space' in your schedule. This acts like extra bandwidth that can be allocated in case of unexpected high-priority tasks.

### The Liberation in Limitation

There's a paradoxical freedom in understanding your limitations. When you know your boundaries, both internal and external, you give yourself the room to operate at your best within those limits. Remember, setting boundaries isn't about limiting your potential; it's about making sustainable choices that enable you to achieve your potential over the long term, without crashing.


# The Multitasking Myth

So, what's the fastest way to saturate yourself with tasks? Trying to handle too many of them at once. Many people think they are good at multitasking, but your bandwidth remains unchanged. You may think you have enough to share between a few light tasks, but consider what happens when you multitask:

### Cleaning the kitchen

To illustrate, lets consider the tasks you need to do when cleaning the kitchen:

* Wipe countertops
* Rinse dishes and load into dishwasher
* Handwash delicate dishes
* Wipe down sink
* Scrub and wipe stovetop
* Sweep and mop floor

Ok, so in one hand you'll hand a broom, and in the other you'll have a cloth to wipe the counters, and with your right foot you'll mop the floor, and then just use your left to balance, right? No, that would be silly (but if you do, I'm impressed). You prioritize what tasks need to happen, and complete them one at a time. You can't wipe down the sink until the dishes are done, so you do the dishes first. You can't sweep the floors while you're doing the dishes, so you figure which one should happen first and do that. Rethinking this, our to-do list should look more like:

1. Rinse dishes and load into dishwasher
2. Handwash delicate dishes
3. Wipe out sink
4. Scrub and wipe stovetop
5. Wipe countertops
6. Sweep and mop floor

Notice the numbers. Before step 2, we need to finish step 1. Before step 3, we need to finish step 2.&#x20;

### How does this relate to me?

Your day-to-day in the IT industry should be planned exactly the same as planning your kitchen tidying. Can you check your emails and listen to your voicemails at the same time? Can you diagnose a computer while entering time entry notes? No, those need to happen one at a time to give each proper attention.&#x20;

But what if you already multitask? Well, you probably aren't. Lets us the example of checking your emails while catching up on voicemails, which you may have done before. Can you reasonably comprehend what is being said verbally while reading an entirely separate set of words? Probably not. What you're likely doing is reading a bit of the email, rapidly switching to listening to the voicemail audio, and then switching back to the email, over and over again in small slices. But uh-oh, when your attention shifted to the email, you missed a key bit of that voicemail audio. And when switching back to reading that email, the tiny processing hit when swapping to a new task makes you miss comprehension on a key element too.&#x20;

This isn't just anecdotal, there's several studies that show not only is it very difficult for humans to effectively multitask but there is also a measurable cognitive "cost" to switching tasks. [In a paper on task switching](https://pubmed.ncbi.nlm.nih.gov/12639695/), researchers found a delay in processing up to 500ms when starting a new task, plus a "residual" cost at the end of that task. [In another study](https://libres.uncg.edu/ir/asu/f/Emery_Lisa_2001_Interdependence_of_Nonoverlapping.pdf), it was found that when participants tried to tackle both a language comprehension task and a spatial task simultaneously, the brain regions responsible for both tasks showed decreased activation. In addition to the cost from switching tasks alone, trying to perform multiple tasks at once can lead to a competition for mental resources.&#x20;


