Skip to content

Commit 11e2afc

Browse files
committed
Site updates, flows, and more
1 parent 1589255 commit 11e2afc

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

64 files changed

+358
-21
lines changed

_config.yml

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@ timezone: America/Chicago
1414
# jekyll-seo-tag settings › https://github.com/jekyll/jekyll-seo-tag/blob/master/docs/usage.md
1515
# ↓ --------------------------
1616

17-
title: Elder Dev Guy # the main title
17+
title: Codes with Coffee # the main title
1818

19-
tagline: Ramblings of an old Dev Ops guy # it will display as the subtitle
19+
tagline: Building things one sip at a time. # it will display as the subtitle
2020

2121
description: >- # used by seo meta and the atom feed
2222
Blog site for Thomas Sloan, the Elder Dev Guy
@@ -98,7 +98,7 @@ theme_mode: # [light | dark]
9898
cdn:
9999

100100
# the avatar on sidebar, support local or CORS resources
101-
avatar: assets/blog-pic_112.png
101+
avatar: assets/blog-pic_112-2.png
102102

103103
# The URL of the site-wide social preview image used in SEO `og:image` meta tag.
104104
# It can be overridden by a customized `page.image` in front matter.
@@ -119,13 +119,13 @@ comments:
119119
issue_term: # < url | pathname | title | ...>
120120
# Giscus options › https://giscus.app
121121
giscus:
122-
repo: # <gh-username>/<repo>
123-
repo_id:
124-
category:
125-
category_id:
122+
repo: "atxcoder/atxcoder.github.io"
123+
repo_id: "R_kgDOP8BzZQ"
124+
category: "Announcements"
125+
category_id: "DIC_kwDOP8BzZc4Cx01j"
126126
mapping: # optional, default to 'pathname'
127127
strict: # optional, default to '0'
128-
input_position: # optional, default to 'bottom'
128+
input_position: "top"
129129
lang: # optional, default to the value of `site.lang`
130130
reactions_enabled: # optional, default to the value of `1`
131131

@@ -174,6 +174,10 @@ collections:
174174
output: true
175175
sort_by: order
176176
permalink: /:collection/:path/:title
177+
flows:
178+
output: true
179+
sort_by: order
180+
permalink: /:collection/:title
177181

178182
defaults:
179183
- scope:
@@ -190,6 +194,7 @@ defaults:
190194
path: _drafts
191195
values:
192196
comments: false
197+
toc: true
193198
- scope:
194199
path: ""
195200
type: tabs # see `site.collections`

_flows/find-guest-accounts.md

Lines changed: 232 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
1+
---
2+
layout: post
3+
title: "Finding guest accounts in your SharePoint site"
4+
date: 2025-11-11 17:03:03
5+
categories: [Automation, Power Automate]
6+
tags: [automation, power-automate, flow]
7+
toc: true
8+
cup_level: 2
9+
description: Using Power Automate to quickly find guest accounts that have access to your SharePoint site
10+
---
11+
12+
In SharePoint, when you share a site, document, or list with someone outside your organization, that person becomes a guest. Behind the scenes, SharePoint leans on Azure Active Directory (AAD) to manage that guest account. So, they’re not just floating around in your site—they have a proper guest account in your Azure AD tenant.
13+
14+
This guest account lets them sign in, access only what you’ve shared, and makes it easy to track them for security and auditing. Think of Azure AD as the gatekeeper and SharePoint as one of the rooms guests are allowed to enter. You control the keys using things like conditional access, multi-factor authentication, or expiration policies.
15+
16+
Keeping track of guests can get tricky. You could dig through SharePoint groups manually, but why make life hard? I built a simple, no-code flow that scans your SharePoint groups and flags any guest accounts automatically. It’s quick, easy to set up, and a solid little tool to keep your site secure without breaking a sweat.
17+
18+
## Trigger
19+
20+
The trigger we'll use is manual. Add an input to capture the URL of the SharePoint site you want to scan for guests.
21+
22+
![1](/assets/img/tutorials/find-guest-accounts/1.png)
23+
24+
## Actions
25+
26+
The first action we'll use is the **Initialize variable** action. This built-in action allows us to create a variable that we can later update in our flow.
27+
28+
Click on the plus sign under your trigger. On the left-hand side search for the **Initialize variable** action and add it to your flow. You will need to give it a name and pick the type of variable. For this flow we'll call it "guestList" and it will be an **array** variable.
29+
30+
![var](/assets/img/tutorials/find-guest-accounts/array-variable-properties.png)
31+
32+
The next action to add is the [Send an HTTP request to SharePoint](https://learn.microsoft.com/en-us/connectors/sharepointonline/#send-an-http-request-to-sharepoint) action that uses the [SharePoint](https://learn.microsoft.com/en-us/connectors/sharepointonline) connector.
33+
34+
![step-2](/assets/img/tutorials/find-guest-accounts/2.png)
35+
36+
For the *Parameters* of this action we are going to add the URL of the SharePoint site that you collected in the trigger. Start by clicking on the **Site Address** property. You should see a drop-down list of SharePoint sites. Scroll all the way to the bottom and select *Custom value*. Now click in the **Site Address** box again and a message should appear. Press "/" and select dynamic value.
37+
38+
![step-3](/assets/img/tutorials/find-guest-accounts/3.png)
39+
40+
A list of previous actions and the trigger will show. Select the trigger ("Manually trigger a flow") and click on *See more* next to it.
41+
42+
![4](/assets/img/tutorials/find-guest-accounts/4.png)
43+
44+
Select the "site_url", or whatever you named your trigger input.
45+
46+
![4-1](/assets/img/tutorials/find-guest-accounts/4-1.png)
47+
48+
For the **Uri** enter the following exactly as shown including the forward slash as the start.
49+
50+
```
51+
/api/web/sitegroups?$expand=users
52+
```
53+
54+
You can leave everything else as-is and you should be left with an action that looks like this.
55+
56+
![9](/assets/img/tutorials/find-guest-accounts/9.png)
57+
58+
If you want, you can test the flow now. Click on **Test** in the top right of the screen, choose "Manually", then **Save & Test** at the bottom. You may be prompted to connect your account — do so.
59+
60+
You will see a box appear asking you to input the site address. It should look similar to this:
61+
62+
![6](/assets/img/tutorials/find-guest-accounts/6.png)
63+
64+
Enter the address of the site collection. Click OK and the flow should run. If everything is successful, move on to the next step. Otherwise, review the steps above and make sure your trigger and action settings match.
65+
66+
## Looping through the groups
67+
68+
If you click on the **Send an HTTP request to SharePoint** action and open **Output**, you can see the JSON returned by SharePoint. Buried in that response are:
69+
70+
1. All the SharePoint groups for that site
71+
2. Each user within each of those groups
72+
73+
Now, we need to loop through each group and then through each user looking for external/guest users. Add a new action and search for **Control**. Add the **Apply to each** action.
74+
75+
![11](/assets/img/tutorials/find-guest-accounts/11.png)
76+
77+
Click on the action to open its properties. Click in the input where it says "Select an output from the previous steps" and then click on the **function** icon.
78+
79+
![16](/assets/img/tutorials/find-guest-accounts/16.png)
80+
81+
In the pop-up, click on **Dynamic content** and then select `Body` from the *Send an HTTP request to SharePoint*. This will add the response content from the *Send an HTTP request to SharePoint* node.
82+
83+
![17](/assets/img/tutorials/find-guest-accounts/17.png)
84+
85+
Since we only want to loop through each group, we need to adjust the code to only look at the *results* array. Add the following:
86+
87+
```
88+
?['d']?['results']
89+
```
90+
91+
Once added, it should look like the image below.
92+
93+
![18](/assets/img/tutorials/find-guest-accounts/18.png)
94+
95+
Click on **Update**.
96+
97+
## Looping through the users
98+
99+
Inside the **Apply to each** action we'll add another **Apply to each** action.
100+
101+
![19](/assets/img/tutorials/find-guest-accounts/19.png)
102+
103+
Follow the steps as before, adding a function to the action. This time, instead of picking the output of a previous action, scroll to the bottom and select **Current item**. This means we are looking at the current item (in our case the SharePoint group).
104+
105+
![14](/assets/img/tutorials/find-guest-accounts/14.png)
106+
107+
Now we need to update the code again to focus on just the users in the current SharePoint group we are looking at.
108+
109+
```
110+
items('Apply_to_each')?['Users']?['results']
111+
```
112+
113+
This code tells it to look at each result in the "results" array under "Users". This is because each user will be a separate item in the results array.
114+
115+
### Finding guest accounts
116+
117+
Now we can finally start looking for guest accounts. First, let's take a look at what *user* properties are available to us. Below is an example of a fake guest user I temporarily added to one of my SharePoint sites.
118+
119+
![user-props](/assets/img/tutorials/find-guest-accounts/user-props.png)
120+
121+
There are a few properties here that identify this user as a guest:
122+
123+
- Email
124+
- IsEmailAuthenticationGuestUser
125+
- IsShareByEmailGuestUser
126+
- LoginName
127+
- UserPrincipalName
128+
129+
We are going to focus on the `LoginName` for this example. Specifically, we'll look for the **`#ext#`** substring. This is the tag Azure AD adds to indicate an external account.
130+
131+
Back in our flow, let's add another action: the **Condition** action. This is a Control action, so search for Control and add it.
132+
133+
You need to add this action to the last **Apply to each** action that you added earlier.
134+
135+
![20](/assets/img/tutorials/find-guest-accounts/20.png)
136+
137+
Click on "Condition" and then the "Choose a value" box in the Condition's properties. Like before, add a function and then add *Dynamic content*. Select the *Current item* from the **Apply to each 1**. The current item is the current user we are looking at in our loop.
138+
139+
![21](/assets/img/tutorials/find-guest-accounts/21.png)
140+
141+
Update the code so it looks like this
142+
143+
```
144+
items('Apply_to_each_1')?['LoginName']
145+
```
146+
147+
Then click on *Add*.
148+
149+
We need to check if the LoginName contains the string "#ext". Select `contains` in the dropdown list. Finally enter the string *#ext*. It should look like this
150+
151+
![contains](/assets/img/tutorials/find-guest-accounts/contains.png)
152+
153+
### Updating our variable
154+
155+
Recall the array variable we created at the very beginning of the flow. We will update it in the **True** branch of our Condition action.
156+
157+
Click the plus button in the *True* branch and add a new action called **Append to array variable**.
158+
159+
![append-to-array](/assets/img/tutorials/find-guest-accounts/true-action.png)
160+
161+
In the properties select the array variable we created at the start of our flow. Enter the following in the *Value* field:
162+
163+
```
164+
{
165+
"Group": "@{items('Apply_to_each')?['Title']}",
166+
"User": "@{items('Apply_to_each_1')?['Title']}",
167+
"Email": "@{items('Apply_to_each_1')?['Email']}"
168+
}
169+
```
170+
171+
Let's go over what this is doing. We are creating a JSON object that we append to the array variable. A JSON object will be created for each guest account the flow finds.
172+
173+
The **Group** key's value is the *Title* of the SharePoint group that we are currently looking at. The **User** key's value is the *Title* (display name) of the current user. Finally, the **Email** key's value is the email of the current user.
174+
175+
The `{` `}` mark the start and end of a JSON object.
176+
177+
If you copied and pasted the above code into the value field, you should see something like this:
178+
179+
![append-props](/assets/img/tutorials/find-guest-accounts/append-props.png)
180+
181+
## Emailing the report
182+
183+
If we run the flow we will end up with a JSON array of objects — one for each guest account. Now we need to format that into something more usable for an end user, such as a CSV file, and attach that CSV to an email.
184+
185+
Add an action called **Create a CSV table** to the very end of your flow. Make sure you don't add it inside any loops.
186+
187+
![csv-1](/assets/img/tutorials/find-guest-accounts/csv-1.png)
188+
189+
For the action parameters, in the *From* box add a dynamic value and select the guestList array. That's all the action needs to create a CSV table. You can change the "Column" header names under Advanced settings, but for now leave them as the defaults. It will automatically use the keys in the array ("Group", "User", "Email") as the column header names. You can always change the key names if you prefer different headers.
190+
191+
The last step in our flow is to email this report to a user.
192+
193+
In this example we hardcode the recipient. You can instead add another trigger input for the recipient email and use that value. Provide an email in that input or reference it in the action.
194+
195+
Add the **Send an email (V2)** action as the very last action in the flow. There are multiple connectors that allow you to send an email; the one shown here is the Office 365 Outlook connector.
196+
197+
![send-email](/assets/img/tutorials/find-guest-accounts/send-email.png)
198+
199+
>Note that connectors are updated over time and you may find multiple versioned actions. At the time of writing, "V2" of the Send an email action was the latest version.
200+
{: .prompt-info}
201+
202+
The parameters for this action are pretty self-explanatory. There are a few to focus on:
203+
204+
**Body**: For the body I like to include the site the report was run against. Use the trigger input name to insert the site URL (for example @{triggerBody()?['site_url']}).
205+
206+
```
207+
Please see the attached report fof guest accounts found for the SharePoint site at @{triggerBody()?['text']}
208+
```
209+
210+
**Attachments**: Under *Advanced parameters* select *Attachments*.
211+
212+
![add-item](/assets/img/tutorials/find-guest-accounts/add%20item.png)
213+
214+
Click on **Add new item**. In the **Name** field provide a name for the attachment. Make sure it ends with *.csv*; for example, `GuestReport.csv`.
215+
216+
For the **Content** add a dynamic value and select the output of the **Create CSV table** action.
217+
218+
![csv-table-output](/assets/img/tutorials/find-guest-accounts/csv-input.png)
219+
220+
It should look like this
221+
222+
![email-done](/assets/img/tutorials/find-guest-accounts/email-done.png)
223+
224+
## Testing the flow
225+
226+
Save and test the flow. Provide the trigger with the URL of a SharePoint site that you have access to (preferably Site Collection Admin or Owner access) and one that has known guest accounts in its SharePoint groups. The flow should run and send you an email with a CSV attachment that contains all the guest accounts on the site.
227+
228+
![email](/assets/img/tutorials/find-guest-accounts/email-blur.png)
229+
230+
Here is what the report looks like
231+
232+
![email-report](/assets/img/tutorials/find-guest-accounts/email-report-blur.png)

_layouts/custom/flows.html

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
---
2+
layout: default
3+
refactor: true
4+
---
5+
{% include lang.html %}
6+
7+
<style>
8+
.posts-grid {
9+
display: grid;
10+
grid-template-columns: 1fr;
11+
row-gap: 1rem;
12+
margin: 2rem 0;
13+
}
14+
15+
.post {
16+
background: #fafafa;
17+
border: 1px solid #e2e2e2;
18+
padding: 1rem;
19+
border-radius: 8px;
20+
transition: transform 0.2s ease;
21+
}
22+
23+
.post:hover {
24+
transform: translateY(-4px);
25+
}
26+
.shimmer::before{
27+
background:none;
28+
}
29+
</style>
30+
31+
<h1>Flows</h1>
32+
33+
<p>Here you will find written tutorials on how to build and implement flows on a number of different platforms. Each flow is rated using my <a href="/coffee-meter">Coffee Mug Rating System</a> </p>
34+
35+
<section class="posts-grid">
36+
{% for post in site.flows %}
37+
<article class="post" >
38+
<h2>
39+
<a href="{{ post.url | relative_url }}">{{ post.title }}</a>
40+
</h2>
41+
{% if post.cup_level %}
42+
<img src="assets/img/flow-cups/{{post.cup_level}}-cup-flow.png" w="150" h="100" style="border-radius: 15px;">
43+
{% endif %}
44+
<p>{{ post.description | strip_html | truncate: 160 }}</p>
45+
</article>
46+
{% endfor %}
47+
</section>
48+
49+
50+
<!-- #post-list -->
51+
52+
{% if paginator.total_pages > 1 %}
53+
{% include post-paginator.html %}
54+
{% endif %}

_layouts/post.html

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@
3232
</div>
3333
{% endif %}
3434
<h1 data-toc-skip>{{ page.title }}</h1>
35+
{% if page.cup_level %}
36+
<img src="assets/img/flow-cups/{{page.cup_level}}-cup-flow.png" w="200" h="100" style="border-radius: 15px;">
37+
{% endif %}
3538
{% if page.description %}
3639
<p class="post-desc fw-light mb-4">{{ page.description }}</p>
3740
{% endif %}

_posts/2025-10-26-microsoft-planner.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
title: Microsoft Planner
33
description: If you’ve ever felt overwhelmed keeping track of tasks across multiple projects, Microsoft Planner might just be the tool you didn’t know you needed. Here’s why I’ve started using it and how it keeps my team organized.
44
image: /assets/img/ms-planner-01.png
5+
date: 2025-11-09 17:08:54
56
categories: ["Microsoft","Planner"]
67
tags: ["productivity"]
78
---

_posts/2025-10-9-tutorials.md

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,24 @@
11
---
2-
title: Tutorials
3-
description: Moved all my tutorials to a single place.
2+
title: 📰 Site News and Developments
3+
description: Latest news and developments for this site.
44
pin: true
55
categories: [Blog,Posts]
6-
tags: [tutorials, general]
6+
tags: [news, general]
77
---
88

9-
I have decided to move all my tutorial post to my new [tutorials page]({% link _tabs/tutorials.md %}). I'll keep the post pinned and updated.
109

11-
## Current Tutorials
12-
- Power Automate 101
10+
## Tutorials
1311

14-
## Upcomming
15-
- Node-RED
12+
You can find my tutorials on the [tutorials page]({% link _tabs/tutorials.md %}). Right now these mainly focus on learning platforms or subjects that span multiple posts.
13+
14+
## Flows
15+
16+
The [Flows page]({% link _tabs/flows.md %}) contains my posts on building automations or "flows" on a number of different platforms. They are kind of like tutorials, but are:
17+
18+
- Shorter
19+
- Focus on a single objective
20+
- Assume some familiarity with the platform (Power Automate, Node-RED, n8n, etc.)
21+
22+
I am also thinking about starting a YouTube channel where I build these automations. If I do that, you will see those videos embeded in their respective flow posts.
23+
24+
I'll keep this post pinned and updated with the latest news for the site!

0 commit comments

Comments
 (0)