Files
leecat.art/_site/azure-locations-and-file-crawling/index.html
2026-02-20 12:24:10 -08:00

1781 lines
73 KiB
HTML

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>azure locations and file crawling | hello hello</title>
<meta name="description" content="Lee Cattarin... on the internet!">
<link rel="alternate" href="/feed.xml" type="application/atom+xml" title="hello hello">
<meta property="og:title" content="azure locations and file crawling">
<meta property="og:type" content="website">
<meta property="og:description" content="Lee Cattarin... on the internet!">
<meta property="og:site_name" content="hello hello">
<meta property="og:image" content="/img/azure-locations.jpg">
<meta property="og:image:alt" content="A Linux terminal. There is a fun rainbow flag in ascii art at the top, and then the user has called a command asking Azure for a list of resources applicable to a specific resource type">
<meta name="generator" content="Eleventy v3.1.2">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="">
<link href="https://fonts.googleapis.com/css2?family=Atkinson+Hyperlegible+Mono:ital,wght@0,200..800;1,200..800&family=Atkinson+Hyperlegible+Next:ital,wght@0,200..800;1,200..800&display=swap" rel="stylesheet">
<script src="https://kit.fontawesome.com/884dded219.js" crossorigin="anonymous"></script>
<style>.post-metadata {
display: flex;
flex-flow: row wrap;
justify-content: space-between;
align-items: baseline;
margin: 1.5rem 0 .5rem;
}
.post-metadata p {
font-size: .9rem;
margin: 0;
}
.post-tags {
display: flex;
flex-flow: row wrap;
gap: .5rem;
list-style: none;
margin: 0;
}
.post-tags li {
margin: 0;
}
.post-tags li a {
text-decoration: none;
color: var(--color-teal);
padding: 0 .5rem;
border-radius: 1rem;
box-shadow: .15rem .15rem var(--color-shadow);
border: .08rem solid var(--color-teal);
line-height: 2;
/* Click animation handling */
position: relative;
top: 0;
left: 0;
transition: top .1s ease-in, left .1s ease-in;
}
.post-tags li a:focus-visible {
outline: none;
background-color: var(--color-teal);
color: var(--color-bg);
}
@media (any-hover: hover) {
.post-tags li a:hover {
outline: none;
background-color: var(--color-teal);
color: var(--color-bg);
}
}
@media (forced-colors: active) {
.post-tags li a:focus-visible {
outline-offset: .08rem;
outline: .08rem solid;
}
@media (any-hover: hover) {
.post-tags li a:hover {
outline-offset: .08rem;
outline: .08rem solid;
}
}
}
/* Click animation */
.post-tags li a:active {
top: .1rem;
left: .1rem;
box-shadow: .05rem .05rem var(--color-shadow);
}
/* Adapted from PrismJS 1.30.0 Tomorrow Night theme
https://prismjs.com/download
*/
code,
pre,
code[class*=language-],
pre[class*=language-] {
font-family: var(--font-family-code);
background-color: var(--color-bg-alt);
font-size: .9rem;
text-shadow: 0 1px var(--color-shadow);
-moz-tab-size: 4;
-o-tab-size: 4;
tab-size: 4;
-webkit-hyphens: none;
-moz-hyphens: none;
-ms-hyphens: none;
hyphens: none;
}
pre,
pre[class*=language-] {
margin: 1rem 0;
padding: 1rem;
line-height: 1.5;
white-space: pre-wrap;
word-break: break-word;
overflow-wrap: break-word;
border-radius: .5rem;
overflow: auto;
}
:not(pre)>code,
:not(pre)>code[class*=language-] {
padding: .2rem;
border-radius: .25rem;
white-space: normal;
}
/* Selected text */
code ::-moz-selection,
code::-moz-selection,
pre ::-moz-selection,
pre::-moz-selection,
code[class*=language-] ::-moz-selection,
code[class*=language-]::-moz-selection,
pre[class*=language-] ::-moz-selection,
pre[class*=language-]::-moz-selection,
code ::selection,
code::selection,
pre ::selection,
pre::selection,
code[class*=language-] ::selection,
code[class*=language-]::selection,
pre[class*=language-] ::selection,
pre[class*=language-]::selection {
text-shadow: none;
background-color: var(--color-bg);
}
/* Syntax highlighting */
.token.namespace {
opacity: .7;
}
.token.bold,
.token.important {
font-weight:700
}
.token.italic {
font-style:italic
}
.token.block-comment,
.token.cdata,
.token.comment,
.token.doctype,
.token.prolog,
.token.punctuation {
color: var(--color-grey);
}
.token.attr-name,
.token.deleted,
.token.namespace,
.token.tag {
color: var(--color-red);
}
.token.boolean,
.token.function,
.token.number {
color: var(--color-orange);
}
.token.class-name,
.token.constant,
.token.property,
.token.symbol {
color: var(--color-yellow);
}
.token.attr-value,
.token.char,
.token.regex,
.token.string,
.token.variable,
.token.inserted {
color: var(--color-green);
}
.token.entity,
.token.operator,
.token.url,
.token.function-name {
color:var(--color-blue);
}
.token.atrule,
.token.builtin,
.token.important,
.token.keyword,
.token.selector {
color: var(--color-purple);
}
#postlist,
#taglist {
list-style: none;
}
#postlist, .post,
#taglist, .tag {
margin: 0;
}
/* Odd-numbered posts & tag layout/coloration */
.post:nth-child(odd) .postlink,
.tag:nth-child(odd) .taglink {
grid-template-areas:
'img h2'
'img info'
'img .';
grid-template-columns: 45% auto;;
--color-primary: var(--color-teal);
--color-accent: var(--color-pink);
}
/* Even-numbered posts & tags layout/coloration */
.post:nth-child(even) .postlink,
.tag:nth-child(even) .taglink {
grid-template-areas:
'h2 img'
'info img'
'. img';
grid-template-columns: auto 45%;
--color-primary: var(--color-pink);
--color-accent: var(--color-teal);
}
/* Layout for all posts on mobile */
@media (max-width: 650px) {
.post:nth-child(n) .postlink,
.tag:nth-child(n) .taglink {
grid-template-areas:
'img'
'h2'
'info';
grid-template-columns: auto;
}
}
/* Link */
.postlink,
.taglink {
display: grid;
border: .25rem solid var(--color-primary);
border-radius: 1.25rem;
box-shadow: .35rem .35rem var(--color-shadow);
margin: 2rem 0;
text-decoration: none;
/* Click animation handling */
position: relative;
top: 0;
left: 0;
transition: top .05s ease-in, left .05s ease-in;
}
.postlink:focus-visible,
.taglink:focus-visible {
background-color: var(--color-primary);
outline: none;
}
@media (any-hover: hover) {
.postlink:hover,
.taglink:hover {
background-color: var(--color-primary);
}
}
/* Forced colors */
@media (forced-colors: active) {
.postlink:focus-visible,
.taglink:focus-visible {
outline-offset: .25rem;
outline: .25rem solid;
}
@media (any-hover: hover) {
.postlink:hover,
.taglink:hover {
outline-offset: .25rem;
outline: .25rem solid;
}
}
}
/* Click animation */
.postlink:active,
.taglink:active {
box-shadow: none;
top: .2rem;
left: .2rem;
box-shadow: .15rem .15rem var(--color-shadow);
}
/* Post & tag elements */
.post h2, .post img,
.post ul, .post li,
.tag h2, .tag p,
.tag img {
margin: 0;
}
.post h2,
.tag h2 {
grid-area: h2;
padding: .25rem .5rem;
text-transform: uppercase;
font-size: 1.5rem;
color: var(--color-primary);
border-radius: 1rem 1rem 0 0;
border-bottom: .25rem solid var(--color-accent);
}
.post:nth-child(even) h2,
.tag:nth-child(even) h2 {
text-align: right;
}
.postlink:focus-visible h2,
.taglink:focus-visible h2 {
color: var(--color-bg);
border-color: var(--color-bg);
}
@media (any-hover: hover) {
.postlink:hover h2,
.taglink:hover h2 {
color: var(--color-bg);
border-color: var(--color-bg);
}
}
/* Images */
.post img,
.tag-imgs {
grid-area: img;
}
.tag-imgs {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: .15rem;
}
.tag-imgs img {
aspect-ratio: 3 / 2;
object-fit: cover;
}
.missing-image {
width: 100%;
aspect-ratio: 3 / 2;
background-color: var(--color-bg-alt);
border-radius: calc(1rem);
}
.taglink:focus-visible .missing-image {
opacity: .7;
}
@media (any-hover: hover) {
.taglink:hover .missing-image {
opacity: .7;
}
}
/* Post tags */
.postlist-tags {
grid-area: info;
list-style: none;
display: flex;
flex-flow: row wrap;
gap: .5rem;
padding: .5rem;
}
.post:nth-child(odd) .postlist-tags {
justify-content: flex-end;
}
.postlist-tags li,
.tagcount {
background-color: var(--color-primary);
color: var(--color-bg);
padding: 0 .5rem;
border-radius: 1rem;
}
.postlink:focus-visible .postlist-tags li,
.taglink:focus-visible .tagcount {
background-color: var(--color-bg);
color: var(--color-primary);
}
@media (any-hover: hover) {
.postlink:hover .postlist-tags li,
.taglink:hover .tagcount {
background-color: var(--color-bg);
color: var(--color-primary);
}
}
/* Tag count */
.tag p {
grid-area: info;
padding: .5rem;
}
.tag:nth-child(odd) p {
text-align: right;
}
:root {
color-scheme: light dark;
--font-family: 'Atkinson Hyperlegible Next', sans-serif;
--font-family-code: 'Atkinson Hyperlegible Mono', monospace;
--color-dark: #2e303e;
--color-dark-alt: #3c3f52;
--color-light: #ebeeef;
--color-light-alt: #dbe1e3;
--color-teal-dark: #18737b;
--color-teal-light: #25b0bc;
--color-pink-dark: #94195d;
--color-pink-light: #ee9fcb;
--color-shadow: rgba(2, 10, 40, .25);
/* Used for syntax highlighting */
--color-red-light: #f195aa;
--color-orange-light: #fab795;
--color-yellow-light: #fbe6bc;
--color-green-light: #29d398;
--color-blue-light: #26bbd9;
--color-purple-light: #ddaeea;
--color-grey-light: #b9c3c6;
--color-red-dark: #991433;
--color-orange-dark: #883206;
--color-yellow-dark: #6a4906;
--color-green-dark: #125940;
--color-blue-dark: #125663;
--color-purple-dark: #722999;
--color-grey-dark: #4a4b64;
--color-text: light-dark(var(--color-dark), var(--color-light));
--color-bg: light-dark(var(--color-light), var(--color-dark));
--color-text-alt: light-dark(var(--color-dark-alt), var(--color-light-alt));
--color-bg-alt: light-dark(var(--color-light-alt), var(--color-dark-alt));
--color-teal: light-dark(var(--color-teal-dark), var(--color-teal-light));
--color-pink: light-dark(var(--color-pink-dark), var(--color-pink-light));
--color-red: light-dark(var(--color-red-dark), var(--color-red-light));
--color-orange: light-dark(var(--color-orange-dark), var(--color-orange-light));
--color-yellow: light-dark(var(--color-yellow-dark), var(--color-yellow-light));
--color-green: light-dark(var(--color-green-dark), var(--color-green-light));
--color-blue: light-dark(var(--color-blue-dark), var(--color-blue-light));
--color-purple: light-dark(var(--color-purple-dark), var(--color-purple-light));
--color-grey: light-dark(var(--color-grey-dark), var(--color-grey-light));
}
/* Base */
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: var(--font-family);
color: var(--color-text);
background-color: var(--color-bg);
}
main {
width: 60vw;
max-width: 1000px;
margin: 0 auto;
scroll-margin-top: 7rem;
}
@media (max-width: 1050px) {
main {
width: 75vw;
}
}
@media (max-width: 650px) {
main {
width: 92vw;
}
}
/* Headers */
h1, h2, h3, h4, h5, h6 {
line-height: 1.25;
color: var(--color-teal);
}
h1 {
margin-top: 3rem;
font-size: 3.5rem;
text-align: center;
}
h2, h3, h4, h5, h6 {
scroll-margin-top: 5rem;
}
h2 {
margin-top: 2rem;
font-size: 2.2rem;
}
h3 {
margin-top: 1.5rem;
font-size: 1.6rem;
}
@media (max-width: 650px) {
h1 { font-size: 2.8rem; }
h2 { font-size: 1.8rem; }
h3 { font-size: 1.35rem; }
}
h4, h5, h6 {
margin-top: 1rem;
font-size: 1.2rem;
}
/* Images */
img {
display: block;
max-width: 100%;
height: auto;
border-radius: 1rem;
}
/* Paragraphs */
p {
margin: 1.25rem 0;
line-height: 1.4;
}
strong,
b {
font-weight: 900;
}
/* Links */
a {
color: var(--color-text);
border-radius: .25rem;
text-decoration: underline;
text-decoration-style: solid;
text-decoration-thickness: .2em;
text-decoration-color: var(--color-teal);
transition: text-decoration-thickness .5s;
}
a:focus-visible {
text-decoration: none;
outline: .15rem solid var(--color-teal);
}
@media (any-hover: hover) {
a:hover {
text-decoration-thickness: .4em;
}
}
a:active {
text-decoration-thickness: .4em;
}
/* Heading anchors */
a.ha,
span.ha-placeholder {
color: var(--color-pink);
}
span.ha-placeholder {
opacity: .55;
}
/* Lists */
::marker {
color: var(--color-pink);
}
ul, ol, dl, li {
margin-left: 1rem;
}
li {
line-height: 1.2 5;
margin-top: .65rem;
margin-bottom: .65rem;
}
li ul, li ol {
margin: .5rem 0;
}
dt {
font-weight: 900;
margin-top: .5rem;
}
dd {
margin-left: 2rem;
margin-bottom: .75rem;
}
/* Blockquotes */
blockquote {
margin: .5rem 0;
padding: 0 1rem;
border-radius: .25rem 1rem 1rem .25rem;
line-height: 1.25;
border-left: .5rem solid var(--color-pink);
}
blockquote,
blockquote p,
blockquote ol,
blockquote ul {
background-color: var(--color-bg-alt);
padding: .5rem;
}
blockquote p {
margin: 0;
}
/* Tables */
table {
width: 100%;
border-spacing: 0; /* border collapse doesn't play nice with radii */
border-radius: .3rem;
border: thin solid var(--color-pink);
}
th {
color: var(--color-bg);
background-color: var(--color-pink);
}
th code {
color: var(--color-text); /* Yes, I actually do this somewhere */
}
th, td {
padding: .5rem;
text-align: left;
}
tr:nth-child(even) { background-color: var(--color-bg-alt); }
th:not(:first-child) { border-left: thin solid var(--color-bg); }
th:first-child { border-top-left-radius: .25rem; }
th:last-child { border-top-right-radius: .25rem; }
td:not(:first-child) { border-left: thin solid var(--color-pink); }
/* Times */
time {
color: var(--color-grey);
}
/* Horizontal rules */
hr {
border: .25rem solid var(--color-pink);
margin: 2rem 0;
}
/* Used on home, reference, gallery pages */
.centered {
text-align: center;
}
/* Currently only used for resume, but it's generalizable */
.upper {
text-transform: uppercase;
}
/* Header */
header {
position: sticky;
top: 0;
background-color: var(--color-bg);
padding: .75rem 0;
z-index: 10;
border-bottom: thick solid var(--color-teal);
box-shadow: 0 .25rem .15rem var(--color-shadow);
}
/* Header links, pagination links */
header a,
.pagination a,
.webring ul a {
border-radius: 1rem;
border: .125rem solid var(--color-pink);
color: var(--color-pink);
text-decoration: none;
padding: 0 .25rem;
box-shadow: .15rem .15rem var(--color-shadow);
font-size: 1.2rem;
/* Click animation handling */
position: relative;
top: 0;
left: 0;
transition: top .05s ease-in, left .05s ease-in;
}
header a,
.pagination .older a,
.webring .prev a,
.webring .rand a {
padding-right: .35rem;
}
.pagination .newer a,
.webring .next a,
.webring .rand a {
padding-left: .35rem;
}
header a:focus-visible,
.pagination a:focus-visible,
.webring ul a:focus-visible {
color: var(--color-bg);
border-color: var(--color-pink);
background-color: var(--color-pink);
outline: none;
}
@media (any-hover: hover) {
header a:hover,
.pagination a:hover,
.webring ul a:hover {
color: var(--color-bg);
border-color: var(--color-pink);
background-color: var(--color-pink);
}
}
@media (forced-colors: active) {
header a:focus-visible,
.pagination a:focus-visible,
.webring ul a:focus-visible {
outline-offset: .125rem;
outline: .125rem solid;
}
@media (any-hover: hover) {
header a:hover,
.pagination a:hover,
.webring ul a:hover {
outline-offset: .125rem;
outline: .125rem solid;
}
}
}
/* Click animation */
header a:active,
.pagination a:active,
.webring ul a:active {
top: .1rem;
left: .1rem;
box-shadow: .05rem .05rem var(--color-shadow);
}
/* Current page */
header a[aria-current="page"] {
border-color: var(--color-teal);
color: var(--color-teal);
}
header a[aria-current="page"]:focus-visible {
color: var(--color-bg);
border-color: var(--color-teal);
background-color: var(--color-teal);
}
@media (any-hover: hover) {
header a[aria-current="page"]:hover {
color: var(--color-bg);
background-color: var(--color-teal);
border-color: var(--color-teal);
}
}
/* Header link icons, pagination icons */
header i,
.pagination i,
.webring ul i {
color: var(--color-teal);
}
header i,
.pagination .older i,
.webring .prev i,
.webring .rand i:nth-child(1) {
padding-left: .25rem;
}
.pagination .newer i,
.webring .next i,
.webring .rand i:nth-child(2) {
padding-right: .25rem;
}
header a[aria-current="page"] i {
color: var(--color-pink);
}
header a:focus-visible i,
a[aria-current="page"] a:focus-visible i,
.pagination a:focus-visible i,
.webring ul a:focus-visible i {
color: var(--color-bg);
}
@media (any-hover: hover) {
header a:hover i,
header a[aria-current="page"]:hover i,
.pagination a:hover i,
.webring ul a:hover i {
color: var(--color-bg);
}
}
/* Skip link */
#skip {
left: -999px;
position: absolute;
top: auto;
width: 1px;
height: 1px;
overflow: hidden;
z-index: -99;
}
#skip:focus-visible {
display: inline-block;
left: auto;
top: auto;
width: auto;
height: auto;
overflow: auto;
margin: 0 10%;
z-index: 999;
}
/* Nav */
header ul {
display: flex;
list-style: none;
gap: 1rem;
justify-content: center;
}
header ul,
header li {
margin: 0;
}
@media (max-width: 650px) {
.menu-text {
display: none; /* Icons only on small screens */
}
header a {
padding: .15rem .5rem;
}
header i {
padding: 0;
}
}
/* Footer */
footer {
padding: 1rem 0;
font-size: .9rem;
border-top: thick solid var(--color-pink);
}
footer ul {
display: flex;
list-style: none;
gap: .5rem;
justify-content: center;
margin: 0;
}
footer li {
margin: 0;
}
footer li:nth-child(2)::before,
footer li:nth-child(2)::after {
content: " ● " / "";
color: var(--color-teal);
}
@media (max-width: 650px) {
footer ul {
flex-flow: column;
text-align: center;
}
footer li:nth-child(2)::before,
footer li:nth-child(2)::after {
content: none;
}
}
footer a {
text-decoration-color: var(--color-pink);
}
footer a:focus-visible {
outline-color: var(--color-pink);
}
/* Pagination */
.pagination,
.pagination li {
margin: 0;
}
.pagination {
display: grid;
grid-template-columns: repeat(2, 1fr);
grid-template-areas: "older newer";
list-style: none;
margin-top: 3rem;
}
@media (max-width: 650px) {
.post-pagination {
grid-template-columns: 1fr;
grid-template-areas:
"older"
"newer";
gap: .75rem;
}
}
.pagination .older {
grid-area: older;
}
.pagination .newer {
grid-area: newer;
text-align: right;
}
/* webring navigation */
.webring {
margin-bottom: 3rem;
}
.webring ul {
display: flex;
flex-flow: row wrap;
list-style: none;
justify-content: space-around;
gap: .35rem;
}
.webring ul,
.webring li {
margin-left: 0;
}
@media print {
/* Nav elements */
header,
footer,
nav {
display: none !important;
}
/* Base */
body {
background-color: #fff;
color: #000;
}
main {
width: 95vw;
}
h1,h2,h3,h4,h5,h6 {
color: #000;
}
/* Links */
/* Hover is not really necessary, but it's annoying when testing otherwise */
a,
a:hover {
text-decoration-style: dotted;
text-decoration-thickness: .1rem;
text-decoration-color: #000;
}
a::after{
content: " (" attr(href) ")";
}
/* Code */
code,
pre,
code[class*=language-],
pre[class*=language-] {
text-shadow: none;
background-color: var(--color-light);
color: #000 !important;
}
.token.namespace {
opacity: 1;
}
.token.block-comment,
.token.cdata,
.token.comment,
.token.doctype,
.token.prolog,
.token.punctuation,
.token.attr-name,
.token.deleted,
.token.namespace,
.token.tag,
.token.boolean,
.token.function,
.token.number,
.token.class-name,
.token.constant,
.token.property,
.token.symbol,
.token.attr-value,
.token.char,
.token.regex,
.token.string,
.token.variable,
.token.inserted,
.token.entity,
.token.operator,
.token.url,
.token.function-name,
.token.atrule,
.token.builtin,
.token.important,
.token.keyword,
.token.selector {
color: #000;
}
}</style>
<script type="module">// Thank you to https://github.com/daviddarnes/heading-anchors
// Thank you to https://amberwilson.co.uk/blog/are-your-anchor-links-accessible/
let globalInstanceIndex = 0;
class HeadingAnchors extends HTMLElement {
static register(tagName = "heading-anchors", registry = window.customElements) {
if(registry && !registry.get(tagName)) {
registry.define(tagName, this);
}
}
static attributes = {
exclude: "data-ha-exclude",
prefix: "prefix",
content: "content",
}
static classes = {
anchor: "ha",
placeholder: "ha-placeholder",
srOnly: "ha-visualhide",
}
static defaultSelector = "h2,h3,h4,h5,h6";
static css = `
.${HeadingAnchors.classes.srOnly} {
clip: rect(0 0 0 0);
height: 1px;
overflow: hidden;
position: absolute;
width: 1px;
}
.${HeadingAnchors.classes.anchor} {
position: absolute;
left: var(--ha_offsetx);
top: var(--ha_offsety);
text-decoration: none;
opacity: 0;
}
.${HeadingAnchors.classes.placeholder} {
opacity: .3;
}
.${HeadingAnchors.classes.anchor}:is(:focus-within, :hover) {
opacity: 1;
}
.${HeadingAnchors.classes.anchor},
.${HeadingAnchors.classes.placeholder} {
display: inline-block;
padding: 0 .25em;
/* Disable selection of visually hidden label */
-webkit-user-select: none;
user-select: none;
}
@supports (anchor-name: none) {
.${HeadingAnchors.classes.anchor} {
position: absolute;
left: anchor(left);
top: anchor(top);
}
}`;
get supports() {
return "replaceSync" in CSSStyleSheet.prototype;
}
get supportsAnchorPosition() {
return CSS.supports("anchor-name: none");
}
constructor() {
super();
if(!this.supports) {
return;
}
let sheet = new CSSStyleSheet();
sheet.replaceSync(HeadingAnchors.css);
document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet];
this.headingStyles = {};
this.instanceIndex = globalInstanceIndex++;
}
connectedCallback() {
if (!this.supports) {
return;
}
this.headings.forEach((heading, index) => {
if(!heading.hasAttribute(HeadingAnchors.attributes.exclude)) {
let anchor = this.getAnchorElement(heading);
let placeholder = this.getPlaceholderElement();
// Prefers anchor position approach for better accessibility
// https://amberwilson.co.uk/blog/are-your-anchor-links-accessible/
if(this.supportsAnchorPosition) {
let anchorName = `--ha_${this.instanceIndex}_${index}`;
placeholder.style.setProperty("anchor-name", anchorName);
anchor.style.positionAnchor = anchorName;
}
heading.appendChild(placeholder);
heading.after(anchor);
}
});
}
// Polyfill-only
positionAnchorFromPlaceholder(placeholder) {
if(!placeholder) {
return;
}
let heading = placeholder.closest("h1,h2,h3,h4,h5,h6");
if(!heading.nextElementSibling) {
return;
}
// TODO next element could be more defensive
this.positionAnchor(heading.nextElementSibling);
}
// Polyfill-only
positionAnchor(anchor) {
if(!anchor || !anchor.previousElementSibling) {
return;
}
// TODO previous element could be more defensive
let heading = anchor.previousElementSibling;
this.setFontProp(heading, anchor);
if(this.supportsAnchorPosition) {
// quit early
return;
}
let placeholder = heading.querySelector(`.${HeadingAnchors.classes.placeholder}`);
if(placeholder) {
anchor.style.setProperty("--ha_offsetx", `${placeholder.offsetLeft}px`);
anchor.style.setProperty("--ha_offsety", `${placeholder.offsetTop}px`);
}
}
setFontProp(heading, anchor) {
let placeholder = heading.querySelector(`.${HeadingAnchors.classes.placeholder}`);
if(placeholder) {
let style = getComputedStyle(placeholder);
let props = ["font-weight", "font-size", "line-height", "font-family"];
let [weight, size, lh, family] = props.map(name => style.getPropertyValue(name));
anchor.style.setProperty("font", `${weight} ${size}/${lh} ${family}`);
let vars = style.getPropertyValue("font-variation-settings");
if(vars) {
anchor.style.setProperty("font-variation-settings", vars);
}
}
}
getAccessibleTextPrefix() {
// Useful for i18n
return this.getAttribute(HeadingAnchors.attributes.prefix) || "Jump to section titled";
}
getContent() {
if(this.hasAttribute(HeadingAnchors.attributes.content)) {
return this.getAttribute(HeadingAnchors.attributes.content);
}
return "#";
}
// Placeholder nests inside of heading
getPlaceholderElement() {
let ph = document.createElement("span");
ph.setAttribute("aria-hidden", true);
ph.classList.add(HeadingAnchors.classes.placeholder);
let content = this.getContent();
if(content) {
ph.innerHTML = content; // CHANGED HERE
}
ph.addEventListener("mouseover", (e) => {
let placeholder = e.target.closest(`.${HeadingAnchors.classes.placeholder}`);
if(placeholder) {
this.positionAnchorFromPlaceholder(placeholder);
}
});
return ph;
}
getAnchorElement(heading) {
let anchor = document.createElement("a");
anchor.href = `#${heading.id}`;
anchor.classList.add(HeadingAnchors.classes.anchor);
let content = this.getContent();
anchor.innerHTML = `<span class="${HeadingAnchors.classes.srOnly}">${this.getAccessibleTextPrefix()}: ${heading.textContent}</span>${content ? `<span aria-hidden="true">${content}</span>` : ""}`;
anchor.addEventListener("focus", e => {
let anchor = e.target.closest(`.${HeadingAnchors.classes.anchor}`);
if(anchor) {
this.positionAnchor(anchor);
}
});
anchor.addEventListener("mouseover", (e) => {
// when CSS anchor positioning is supported, this is only used to set the font
let anchor = e.target.closest(`.${HeadingAnchors.classes.anchor}`);
this.positionAnchor(anchor);
});
return anchor;
}
get headings() {
return this.querySelectorAll(this.selector.split(",").map(entry => `${entry.trim()}[id]`));
}
get selector() {
return this.getAttribute("selector") || HeadingAnchors.defaultSelector;
}
}
HeadingAnchors.register();
export { HeadingAnchors }</script>
</head>
<body>
<header>
<a href="#main" id="skip" title="skip to main content">
<i class="fa-solid fa-forward" aria-hidden="true"></i> skip
</a>
<nav aria-label="main navigation">
<ul>
<li>
<a href="/reference/" title="read reference posts">
<i class="fa-regular fa-folder-open" aria-hidden="true"></i>
<span class="menu-text">reference</span>
</a>
</li>
<li>
<a href="/gallery/" title="view the gallery">
<i class="fa-regular fa-images" aria-hidden="true"></i>
<span class="menu-text">gallery</span>
</a>
</li>
<li>
<a href="/" title="">
<i class="fa fa-solid fa-crow" aria-hidden="true"></i>
<span class="menu-text">home</span>
</a>
</li>
<li>
<a href="/about/" title="about Lee">
<i class="fa-regular fa-user" aria-hidden="true"></i>
<span class="menu-text">about</span>
</a>
</li>
<li>
<a href="/contact/" title="contact Lee">
<i class="fa-solid fa-envelope-open-text" aria-hidden="true"></i>
<span class="menu-text">contact</span>
</a>
</li>
</ul>
</nav>
</header>
<main id="main">
<heading-anchors content="<i class='fa-solid fa-anchor'></i>">
<article>
<h1 id="azure-locations-and-file-crawling">azure locations and file crawling</h1>
<div class="post-metadata">
<p>
posted on
<time datetime="2025-05-14">May 14, 2025</time>
by Lee Cattarin
</p>
<ul class="post-tags">
<li>
<a href="/tags/software/">software</a>
</li>
<li>
<a href="/tags/highlight/">highlight</a>
</li>
</ul>
</div>
<img src="/img/azure-locations.jpg" alt="A Linux terminal. There is a fun rainbow flag in ascii art at the top, and then the user has called a command asking Azure for a list of resources applicable to a specific resource type" loading="lazy" decoding="async" width="1000" height="827">
<h2 id="context">context</h2>
<p>Azure is Microsoft's cloud offering. Each possible resource that can be deployed in Azure has a location it's deployed in, such as &quot;East US&quot; or &quot;Italy.&quot; While some resources can be deployed in all locations, other resources have location constraints.</p>
<p>It's common, when deploying, to have a whole ecosystem of resources that will work together. However, this introduces a problem: which locations work for all resources in a deployment?</p>
<p>Let's dig in. (Want just the outcome? Check the <a href="#summary">summary</a>.)</p>
<h3 id="bicep">bicep</h3>
<p><a href="https://learn.microsoft.com/azure/azure-resource-manager/bicep/overview?tabs=bicep">Bicep</a> is a language for describing Azure resources. A Bicep file sets out a series of resources with preset or parameterized properties in order to deploy said resources.</p>
<p>A minimal Bicep file that creates a resource group might look like this:</p>
<pre class="language-bicep"><code class="language-bicep"><span class="token keyword">param</span> resourceGroupName <span class="token datatype class-name">string</span> <span class="token operator">=</span> <span class="token string">'myResourceGroup'</span>
<span class="token keyword">param</span> location <span class="token datatype class-name">string</span> <span class="token operator">=</span> "westus2"
<span class="token keyword">resource</span> resourceGroup <span class="token string">'Microsoft.Resources/resourceGroups@2022-09-01'</span> <span class="token operator">=</span> <span class="token punctuation">{</span>
<span class="token property">name</span><span class="token operator">:</span> resourceGroupName
<span class="token property">location</span><span class="token operator">:</span> location
<span class="token punctuation">}</span></code></pre>
<p>This is easy to start parsing - I can use <code>grep</code> to find that <code>Microsoft.Resources/resourceGroups@2022-09-01</code> string and go from there.</p>
<h4 id="nesting">nesting</h4>
<p>However, minimal is uncommon. As stated above, deployments of multiple resources are much more common.</p>
<p>When working with large deployments, certain resources may be needed more than once. You can repeat your earlier storage account declaration, or, instead, you can template out how to deploy a storage account with given parameters, then reuse that template. This is called a <code>module</code>, and it's fundamental to organizing Bicep files.</p>
<p>Let's say this is our file structure. Ignore the lack of parameter files or READMEs, this is just an example.</p>
<pre class="language-txt"><code class="language-txt">.
|--infra
|--env
| |--dev
| | |--main.bicep
| |--prod
| |--main.bicep
|--modules
|--rg
| |--main.bicep
|--vm
| |--modules
| | |--network.bicep
| | |--virtual-machine.bicep
| |--main.bicep
|--kv
|--modules
| |--role-assignment.bicep
| |--key-vault.bicep
|--main.bicep</code></pre>
<p>Bicep files use relative references for local modules, so <code>infra/env/dev/main.bicep</code> references <code>../../modules/vm/main.bicep</code>, which references <code>./modules/network.bicep</code>. While the directory structure in this example <em>could</em> be flattened, my point is: modules can nest, and each module refers relatively to the module(s) it relies on.</p>
<h2 id="finding-all-resources">finding all resources</h2>
<p>Okay, let's backtrack. From a given Bicep file, we want:</p>
<ol>
<li>All referenced resource types</li>
<li>All referenced modules</li>
</ol>
<h3 id="grep">grep</h3>
<p>Resources and modules both have patterns in how they are declared. Thankfully, they're pretty simple regexes. <code>grep</code> will spit out lines in a file that match a given regex.</p>
<pre class="language-sh"><code class="language-sh"><span class="token comment"># this gets us strings like</span>
<span class="token comment"># resource resourceGroup 'Microsoft.Resources/resourceGroups@2022-09-01' = {</span>
<span class="token function">grep</span> <span class="token parameter variable">-E</span> <span class="token string">"^resource "</span> <span class="token string">"<span class="token variable">$file</span>"</span>
<span class="token comment"># this gets us strings like</span>
<span class="token comment"># module vm '../../modules/vm/main.bicep' = {</span>
<span class="token function">grep</span> <span class="token parameter variable">-E</span> <span class="token string">"^module "</span> <span class="token string">"<span class="token variable">$file</span>"</span></code></pre>
<h3 id="cut">cut</h3>
<p>From there, let's use <code>cut</code> to strip off the parts we don't want.</p>
<pre class="language-sh"><code class="language-sh"><span class="token comment"># this gets us strings like</span>
<span class="token comment"># Microsoft.Resources/resourceGroups</span>
<span class="token function">grep</span> <span class="token parameter variable">-E</span> <span class="token string">"^resource "</span> <span class="token string">"<span class="token variable">$file</span>"</span> <span class="token punctuation">\</span>
<span class="token operator">|</span> <span class="token function">cut</span> <span class="token parameter variable">-d</span> <span class="token string">"'"</span> <span class="token parameter variable">-f</span> <span class="token number">2</span> - <span class="token punctuation">\</span>
<span class="token operator">|</span> <span class="token function">cut</span> <span class="token parameter variable">-d</span> <span class="token string">"@"</span> <span class="token parameter variable">-f</span> <span class="token number">1</span> -
<span class="token comment"># this gets us strings like</span>
<span class="token comment"># ../../modules/vm/main.bicep</span>
<span class="token function">grep</span> <span class="token parameter variable">-E</span> <span class="token string">"^module "</span> <span class="token string">"<span class="token variable">$file</span>"</span> <span class="token punctuation">\</span>
<span class="token operator">|</span> <span class="token function">cut</span> <span class="token parameter variable">-d</span> <span class="token string">"'"</span> <span class="token parameter variable">-f</span> <span class="token number">2</span> -</code></pre>
<p>These calls are a little opaque. <code>-d</code> sets a <strong>delimiter</strong> (what to split on). <code>-f</code> picks a <strong>field</strong> to return, numbered from 1.</p>
<h3 id="mapfile">mapfile</h3>
<p>We'll save these values to variables. <code>mapfile</code> reads a file, putting each line into a new array element. <code>-t</code> <strong>trims</strong> newline characters. The <code>&lt;</code>s do some redirection, and yes, the space between them <em>matters</em>.</p>
<pre class="language-sh"><code class="language-sh"><span class="token builtin class-name">mapfile</span> <span class="token parameter variable">-t</span> resources <span class="token operator">&lt;</span> <span class="token operator">&lt;</span><span class="token punctuation">(</span><span class="token function">grep</span> <span class="token parameter variable">-E</span> <span class="token string">"^resource "</span> <span class="token string">"<span class="token variable">$file</span>"</span> <span class="token punctuation">\</span>
<span class="token operator">|</span> <span class="token function">cut</span> <span class="token parameter variable">-d</span> <span class="token string">"'"</span> <span class="token parameter variable">-f</span> <span class="token number">2</span> - <span class="token punctuation">\</span>
<span class="token operator">|</span> <span class="token function">cut</span> <span class="token parameter variable">-d</span> <span class="token string">"@"</span> <span class="token parameter variable">-f</span> <span class="token number">1</span> -<span class="token punctuation">)</span>
<span class="token builtin class-name">mapfile</span> <span class="token parameter variable">-t</span> modules <span class="token operator">&lt;</span> <span class="token operator">&lt;</span><span class="token punctuation">(</span><span class="token function">grep</span> <span class="token parameter variable">-E</span> <span class="token string">"^module "</span> <span class="token string">"<span class="token variable">$file</span>"</span> <span class="token punctuation">\</span>
<span class="token operator">|</span> <span class="token function">cut</span> <span class="token parameter variable">-d</span> <span class="token string">"'"</span> <span class="token parameter variable">-f</span> <span class="token number">2</span> -<span class="token punctuation">)</span></code></pre>
<h3 id="dirname-and-more">dirname (&amp; more)</h3>
<p>We can't just stop there. We need to search each module in turn. Using <code>dirname</code>, we can get the directory of the file we're searching, then append the relative module path.</p>
<pre class="language-sh"><code class="language-sh"><span class="token function-name function">get_resources</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token comment"># ... grep, cut, etc ...</span>
<span class="token assign-left variable">directory</span><span class="token operator">=</span><span class="token variable"><span class="token variable">$(</span><span class="token function">dirname</span> <span class="token string">"<span class="token variable">$file</span>"</span><span class="token variable">)</span></span>
<span class="token keyword">for</span> <span class="token for-or-select variable">module</span> <span class="token keyword">in</span> <span class="token string">"<span class="token variable">${modules<span class="token punctuation">[</span>@<span class="token punctuation">]</span>}</span>"</span>
<span class="token keyword">do</span>
<span class="token builtin class-name">mapfile</span> <span class="token parameter variable">-t</span> <span class="token parameter variable">-O</span> <span class="token string">"<span class="token variable">${<span class="token operator">#</span>resources<span class="token punctuation">[</span>@<span class="token punctuation">]</span>}</span>"</span> resources <span class="token operator">&lt;</span> <span class="token operator">&lt;</span><span class="token punctuation">(</span>get_resources <span class="token string">"<span class="token variable">$directory</span>/<span class="token variable">$module</span>"</span><span class="token punctuation">)</span>
<span class="token keyword">done</span>
<span class="token punctuation">}</span></code></pre>
<p>A lot just happened there besides <code>dirname</code>. <code>{modules[@]}</code> is all the array elements (as opposed to just <code>$modules</code>, which evaluates to the first element). <code>${#modules[@]}</code>, on the other hand - note the pound sign - is the number of elements in the array.</p>
<p>Additionally, <code>mapfile</code> usually writes from index 0 onwards. But with the <code>-O</code> argument, we can specify an <strong>origin</strong>. By setting the starting point to the length of the array, we append to the array rather than writing over existing data.</p>
<p>Finally, we got some recursion going! <code>get_resources</code> calls <code>get_resources</code> for every module found.</p>
<h3 id="the-get-resources-function">the get_resources function</h3>
<p>So far, our code looks like this:</p>
<pre class="language-sh"><code class="language-sh"><span class="token function-name function">get_resources</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token builtin class-name">mapfile</span> <span class="token parameter variable">-t</span> resources <span class="token operator">&lt;</span> <span class="token operator">&lt;</span><span class="token punctuation">(</span><span class="token function">grep</span> <span class="token parameter variable">-E</span> <span class="token string">"^resource "</span> <span class="token string">"<span class="token variable">$file</span>"</span> <span class="token punctuation">\</span>
<span class="token operator">|</span> <span class="token function">cut</span> <span class="token parameter variable">-d</span> <span class="token string">"'"</span> <span class="token parameter variable">-f</span> <span class="token number">2</span> - <span class="token punctuation">\</span>
<span class="token operator">|</span> <span class="token function">cut</span> <span class="token parameter variable">-d</span> <span class="token string">"@"</span> <span class="token parameter variable">-f</span> <span class="token number">1</span> -<span class="token punctuation">)</span>
<span class="token builtin class-name">mapfile</span> <span class="token parameter variable">-t</span> modules <span class="token operator">&lt;</span> <span class="token operator">&lt;</span><span class="token punctuation">(</span><span class="token function">grep</span> <span class="token parameter variable">-E</span> <span class="token string">"^module "</span> <span class="token string">"<span class="token variable">$file</span>"</span> <span class="token punctuation">\</span>
<span class="token operator">|</span> <span class="token function">cut</span> <span class="token parameter variable">-d</span> <span class="token string">"'"</span> <span class="token parameter variable">-f</span> <span class="token number">2</span> -<span class="token punctuation">)</span>
<span class="token assign-left variable">directory</span><span class="token operator">=</span><span class="token variable"><span class="token variable">$(</span><span class="token function">dirname</span> <span class="token string">"<span class="token variable">$file</span>"</span><span class="token variable">)</span></span>
<span class="token keyword">for</span> <span class="token for-or-select variable">module</span> <span class="token keyword">in</span> <span class="token string">"<span class="token variable">${modules<span class="token punctuation">[</span>@<span class="token punctuation">]</span>}</span>"</span>
<span class="token keyword">do</span>
<span class="token builtin class-name">mapfile</span> <span class="token parameter variable">-t</span> <span class="token parameter variable">-O</span> <span class="token string">"<span class="token variable">${<span class="token operator">#</span>resources<span class="token punctuation">[</span>@<span class="token punctuation">]</span>}</span>"</span> resources <span class="token operator">&lt;</span> <span class="token operator">&lt;</span><span class="token punctuation">(</span>get_resources <span class="token string">"<span class="token variable">$directory</span>/<span class="token variable">$module</span>"</span><span class="token punctuation">)</span>
<span class="token keyword">done</span>
<span class="token keyword">for</span> <span class="token for-or-select variable">resource</span> <span class="token keyword">in</span> <span class="token string">"<span class="token variable">${resources<span class="token punctuation">[</span>@<span class="token punctuation">]</span>}</span>"</span><span class="token punctuation">;</span> <span class="token keyword">do</span><span class="token punctuation">;</span> <span class="token builtin class-name">echo</span> <span class="token string">"<span class="token variable">$resource</span>"</span><span class="token punctuation">;</span> <span class="token keyword">done</span>
<span class="token punctuation">}</span></code></pre>
<p>That last one-liner just returns our results. Note that we don't just <code>echo &quot;${resources[@]}&quot;</code> - this results in a space-delimited string and it'll be helpful later to have a newline-delimited string.</p>
<h2 id="finding-locations">finding locations</h2>
<p>Now we need to use these resource types to get available locations. First, actually call our function from above. We'll assume we're in a directory with a top-level <code>main.bicep</code> file.</p>
<pre class="language-sh"><code class="language-sh"><span class="token builtin class-name">mapfile</span> <span class="token parameter variable">-t</span> resources <span class="token operator">&lt;</span> <span class="token operator">&lt;</span><span class="token punctuation">(</span>get_resources <span class="token string">"main.bicep"</span><span class="token punctuation">)</span></code></pre>
<h3 id="sort">sort</h3>
<p>Does sorting matter? Not really, but <code>sort</code> has a useful feature, <code>-u</code>, which returns <strong>u</strong>nique items (aka, it deduplicates). Looking up the same resource type twice slows us down.</p>
<pre class="language-sh"><code class="language-sh"><span class="token builtin class-name">mapfile</span> <span class="token parameter variable">-t</span> resources <span class="token operator">&lt;</span> <span class="token operator">&lt;</span><span class="token punctuation">(</span>get_resources <span class="token string">"main.bicep"</span> <span class="token operator">|</span> <span class="token function">sort</span> -u<span class="token punctuation">)</span></code></pre>
<p><code>sort</code> is one reason it helps to have newlines as delimiters - it expects that.</p>
<h3 id="az">az</h3>
<p>We'll use <code>az</code> to list <em>all</em> the locations - just to give ourselves a starting point. You could also use the locations for the first resource type.</p>
<pre class="language-sh"><code class="language-sh"><span class="token builtin class-name">mapfile</span> <span class="token parameter variable">-t</span> locations <span class="token operator">&lt;</span> <span class="token operator">&lt;</span><span class="token punctuation">(</span>az account list-locations <span class="token parameter variable">--query</span> <span class="token string">"[].displayName"</span> <span class="token punctuation">\</span>
<span class="token parameter variable">--out</span> tsv<span class="token punctuation">)</span></code></pre>
<p>We can then use an <code>az</code> command to find available locations for a given resource type:</p>
<pre class="language-sh"><code class="language-sh"><span class="token builtin class-name">mapfile</span> <span class="token parameter variable">-t</span> newLocations <span class="token operator">&lt;</span> <span class="token operator">&lt;</span><span class="token punctuation">(</span>az provider show <span class="token parameter variable">--namespace</span> <span class="token string">"<span class="token variable">$namespace</span>"</span> <span class="token punctuation">\</span>
<span class="token parameter variable">--query</span> <span class="token string">"resourceTypes[?resourceType=='<span class="token variable">$resourceType</span>'].locations | [0]"</span> <span class="token punctuation">\</span>
<span class="token parameter variable">--out</span> tsv<span class="token punctuation">)</span></code></pre>
<p><code>--out tsv</code> means we will get a list with no decoration whatsoever - it's vital for programmatic handling of <code>az</code> command output.</p>
<h3 id="cut-again">cut (again)</h3>
<p>We'll need to get those <code>$namespace</code> and <code>$resourceType</code> variables. <code>cut</code> comes back in handy:</p>
<pre class="language-sh"><code class="language-sh"><span class="token comment"># remember, $resource is something like Microsoft.Resources/resourceGroups</span>
<span class="token comment"># this gets us strings like</span>
<span class="token comment"># Microsoft.Resources</span>
<span class="token assign-left variable">namespace</span><span class="token operator">=</span><span class="token variable"><span class="token variable">$(</span><span class="token builtin class-name">echo</span> <span class="token string">"<span class="token variable">$resource</span>"</span> <span class="token operator">|</span> <span class="token function">cut</span> <span class="token parameter variable">-d</span> <span class="token string">"/"</span> <span class="token parameter variable">-f</span> <span class="token number">1</span> -<span class="token variable">)</span></span>
<span class="token comment"># this gets us strings like</span>
<span class="token comment"># resourceGroups</span>
<span class="token assign-left variable">resourceType</span><span class="token operator">=</span><span class="token variable"><span class="token variable">$(</span><span class="token builtin class-name">echo</span> <span class="token string">"<span class="token variable">$resource</span>"</span> <span class="token operator">|</span> <span class="token function">cut</span> <span class="token parameter variable">-d</span> <span class="token string">"/"</span> <span class="token parameter variable">-f</span> <span class="token number">2</span> -<span class="token variable">)</span></span></code></pre>
<h3 id="comm">comm</h3>
<p>Okay, we can get locations. How do we handle finding their intersection?</p>
<p><code>comm</code> to the rescue. It finds <strong>common</strong> lines between two <em>sorted</em> files. Its default output is three columns - lines only in file 1, lines only in file 2, and lines common to both. We can suppress the first two columns with <code>-12</code>.</p>
<p><code>comm</code> expects files, so we'll reuse our redirection <code>&lt;(someCommand)</code> from earlier.</p>
<pre class="language-sh"><code class="language-sh"><span class="token builtin class-name">mapfile</span> <span class="token parameter variable">-t</span> locations <span class="token operator">&lt;</span> <span class="token operator">&lt;</span><span class="token punctuation">(</span><span class="token function">comm</span> <span class="token parameter variable">-12</span> <span class="token punctuation">\</span>
<span class="token operator">&lt;</span><span class="token punctuation">(</span><span class="token keyword">for</span> <span class="token for-or-select variable">location</span> <span class="token keyword">in</span> <span class="token string">"<span class="token variable">${locations<span class="token punctuation">[</span>@<span class="token punctuation">]</span>}</span>"</span><span class="token punctuation">;</span> <span class="token keyword">do</span> <span class="token builtin class-name">echo</span> <span class="token string">"<span class="token variable">$location</span>"</span><span class="token punctuation">;</span> <span class="token keyword">done</span><span class="token punctuation">)</span> <span class="token punctuation">\</span>
<span class="token operator">&lt;</span><span class="token punctuation">(</span><span class="token keyword">for</span> <span class="token for-or-select variable">location</span> <span class="token keyword">in</span> <span class="token string">"<span class="token variable">${newLocations<span class="token punctuation">[</span>@<span class="token punctuation">]</span>}</span>"</span><span class="token punctuation">;</span> <span class="token keyword">do</span> <span class="token builtin class-name">echo</span> <span class="token string">"<span class="token variable">$location</span>"</span><span class="token punctuation">;</span> <span class="token keyword">done</span><span class="token punctuation">)</span> <span class="token punctuation">)</span></code></pre>
<p><code>comm</code> also likes newline-delimited input, so we're again looping through the array rather than echoing all values at once.</p>
<h3 id="catching-errors">catching errors</h3>
<p>With functionality as it is, many deployments will come back with 0 locations available. Turns out some basic resource types, like role assignments, don't have locations. So let's filter those.</p>
<pre class="language-sh"><code class="language-sh"><span class="token keyword">if</span> <span class="token punctuation">[</span><span class="token punctuation">[</span> <span class="token variable">${<span class="token operator">#</span>newLocations<span class="token punctuation">[</span>@<span class="token punctuation">]</span>}</span> <span class="token parameter variable">-eq</span> <span class="token number">0</span> <span class="token punctuation">]</span><span class="token punctuation">]</span>
<span class="token keyword">then</span>
<span class="token comment"># handle</span>
<span class="token keyword">fi</span></code></pre>
<h3 id="tee">tee</h3>
<p>We'll print the locations to the shell. We can even use <code>tee</code> to print them to a file for good measure:</p>
<pre class="language-sh"><code class="language-sh"><span class="token keyword">for</span> <span class="token for-or-select variable">location</span> <span class="token keyword">in</span> <span class="token string">"<span class="token variable">${locations<span class="token punctuation">[</span>@<span class="token punctuation">]</span>}</span>"</span><span class="token punctuation">;</span> <span class="token keyword">do</span> <span class="token builtin class-name">echo</span> <span class="token string">"<span class="token variable">$location</span>"</span><span class="token punctuation">;</span> <span class="token keyword">done</span> <span class="token operator">|</span> <span class="token function">tee</span> locations.txt</code></pre>
<h3 id="the-location-code">the location code</h3>
<p>Here's our code for this section:</p>
<pre class="language-sh"><code class="language-sh"><span class="token builtin class-name">mapfile</span> <span class="token parameter variable">-t</span> resources <span class="token operator">&lt;</span> <span class="token operator">&lt;</span><span class="token punctuation">(</span>get_resources <span class="token string">"main.bicep"</span> <span class="token operator">|</span> <span class="token function">sort</span> -u<span class="token punctuation">)</span>
<span class="token builtin class-name">mapfile</span> <span class="token parameter variable">-t</span> locations <span class="token operator">&lt;</span> <span class="token operator">&lt;</span><span class="token punctuation">(</span>az account list-locations <span class="token parameter variable">--query</span> <span class="token string">"[].displayName"</span> <span class="token punctuation">\</span>
<span class="token parameter variable">--out</span> tsv<span class="token punctuation">)</span>
<span class="token keyword">for</span> <span class="token for-or-select variable">resource</span> <span class="token keyword">in</span> <span class="token string">"<span class="token variable">${resources<span class="token punctuation">[</span>@<span class="token punctuation">]</span>}</span>"</span>
<span class="token keyword">do</span>
<span class="token assign-left variable">namespace</span><span class="token operator">=</span><span class="token variable"><span class="token variable">$(</span><span class="token builtin class-name">echo</span> <span class="token string">"<span class="token variable">$resource</span>"</span> <span class="token operator">|</span> <span class="token function">cut</span> <span class="token parameter variable">-d</span> <span class="token string">"/"</span> <span class="token parameter variable">-f</span> <span class="token number">1</span> -<span class="token variable">)</span></span>
<span class="token assign-left variable">resourceType</span><span class="token operator">=</span><span class="token variable"><span class="token variable">$(</span><span class="token builtin class-name">echo</span> <span class="token string">"<span class="token variable">$resource</span>"</span> <span class="token operator">|</span> <span class="token function">cut</span> <span class="token parameter variable">-d</span> <span class="token string">"/"</span> <span class="token parameter variable">-f</span> <span class="token number">2</span> -<span class="token variable">)</span></span>
<span class="token builtin class-name">mapfile</span> <span class="token parameter variable">-t</span> newLocations <span class="token operator">&lt;</span> <span class="token operator">&lt;</span><span class="token punctuation">(</span>az provider show <span class="token parameter variable">--namespace</span> <span class="token string">"<span class="token variable">$namespace</span>"</span> <span class="token punctuation">\</span>
<span class="token parameter variable">--query</span> <span class="token string">"resourceTypes[?resourceType=='<span class="token variable">$resourceType</span>'].locations | [0]"</span> <span class="token punctuation">\</span>
<span class="token parameter variable">--out</span> tsv<span class="token punctuation">)</span>
<span class="token keyword">if</span> <span class="token punctuation">[</span><span class="token punctuation">[</span> <span class="token variable">${<span class="token operator">#</span>newLocations<span class="token punctuation">[</span>@<span class="token punctuation">]</span>}</span> <span class="token parameter variable">-eq</span> <span class="token number">0</span> <span class="token punctuation">]</span><span class="token punctuation">]</span>
<span class="token keyword">then</span>
<span class="token builtin class-name">continue</span>
<span class="token keyword">fi</span>
<span class="token builtin class-name">mapfile</span> <span class="token parameter variable">-t</span> locations <span class="token operator">&lt;</span> <span class="token operator">&lt;</span><span class="token punctuation">(</span><span class="token function">comm</span> <span class="token parameter variable">-12</span> <span class="token punctuation">\</span>
<span class="token operator">&lt;</span><span class="token punctuation">(</span><span class="token keyword">for</span> <span class="token for-or-select variable">location</span> <span class="token keyword">in</span> <span class="token string">"<span class="token variable">${locations<span class="token punctuation">[</span>@<span class="token punctuation">]</span>}</span>"</span><span class="token punctuation">;</span> <span class="token keyword">do</span> <span class="token builtin class-name">echo</span> <span class="token string">"<span class="token variable">$location</span>"</span><span class="token punctuation">;</span> <span class="token keyword">done</span><span class="token punctuation">)</span> <span class="token punctuation">\</span>
<span class="token operator">&lt;</span><span class="token punctuation">(</span><span class="token keyword">for</span> <span class="token for-or-select variable">location</span> <span class="token keyword">in</span> <span class="token string">"<span class="token variable">${newLocations<span class="token punctuation">[</span>@<span class="token punctuation">]</span>}</span>"</span><span class="token punctuation">;</span> <span class="token keyword">do</span> <span class="token builtin class-name">echo</span> <span class="token string">"<span class="token variable">$location</span>"</span><span class="token punctuation">;</span> <span class="token keyword">done</span><span class="token punctuation">)</span> <span class="token punctuation">)</span>
<span class="token keyword">done</span>
<span class="token keyword">for</span> <span class="token for-or-select variable">location</span> <span class="token keyword">in</span> <span class="token string">"<span class="token variable">${locations<span class="token punctuation">[</span>@<span class="token punctuation">]</span>}</span>"</span><span class="token punctuation">;</span> <span class="token keyword">do</span> <span class="token builtin class-name">echo</span> <span class="token string">"<span class="token variable">$location</span>"</span><span class="token punctuation">;</span> <span class="token keyword">done</span> <span class="token operator">|</span> <span class="token function">tee</span> locations.txt</code></pre>
<hr>
<h2 id="summary">summary</h2>
<p>Here's our final script:</p>
<pre class="language-sh"><code class="language-sh"><span class="token comment"># Recursively crawls bicep files to find all referenced resources</span>
<span class="token function-name function">get_resources</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token builtin class-name">mapfile</span> <span class="token parameter variable">-t</span> resources <span class="token operator">&lt;</span> <span class="token operator">&lt;</span><span class="token punctuation">(</span><span class="token function">grep</span> <span class="token parameter variable">-E</span> <span class="token string">"^resource "</span> <span class="token string">"<span class="token variable">$file</span>"</span> <span class="token punctuation">\</span>
<span class="token operator">|</span> <span class="token function">cut</span> <span class="token parameter variable">-d</span> <span class="token string">"'"</span> <span class="token parameter variable">-f</span> <span class="token number">2</span> - <span class="token punctuation">\</span>
<span class="token operator">|</span> <span class="token function">cut</span> <span class="token parameter variable">-d</span> <span class="token string">"@"</span> <span class="token parameter variable">-f</span> <span class="token number">1</span> -<span class="token punctuation">)</span>
<span class="token builtin class-name">mapfile</span> <span class="token parameter variable">-t</span> modules <span class="token operator">&lt;</span> <span class="token operator">&lt;</span><span class="token punctuation">(</span><span class="token function">grep</span> <span class="token parameter variable">-E</span> <span class="token string">"^module "</span> <span class="token string">"<span class="token variable">$file</span>"</span> <span class="token punctuation">\</span>
<span class="token operator">|</span> <span class="token function">cut</span> <span class="token parameter variable">-d</span> <span class="token string">"'"</span> <span class="token parameter variable">-f</span> <span class="token number">2</span> -<span class="token punctuation">)</span>
<span class="token assign-left variable">directory</span><span class="token operator">=</span><span class="token variable"><span class="token variable">$(</span><span class="token function">dirname</span> <span class="token string">"<span class="token variable">$file</span>"</span><span class="token variable">)</span></span>
<span class="token keyword">for</span> <span class="token for-or-select variable">module</span> <span class="token keyword">in</span> <span class="token string">"<span class="token variable">${modules<span class="token punctuation">[</span>@<span class="token punctuation">]</span>}</span>"</span>
<span class="token keyword">do</span>
<span class="token builtin class-name">mapfile</span> <span class="token parameter variable">-t</span> <span class="token parameter variable">-O</span> <span class="token string">"<span class="token variable">${<span class="token operator">#</span>resources<span class="token punctuation">[</span>@<span class="token punctuation">]</span>}</span>"</span> resources <span class="token operator">&lt;</span> <span class="token operator">&lt;</span><span class="token punctuation">(</span>get_resources <span class="token string">"<span class="token variable">$directory</span>/module"</span><span class="token punctuation">)</span>
<span class="token keyword">done</span>
<span class="token keyword">for</span> <span class="token for-or-select variable">resource</span> <span class="token keyword">in</span> <span class="token string">"<span class="token variable">${resources<span class="token punctuation">[</span>@<span class="token punctuation">]</span>}</span>"</span><span class="token punctuation">;</span> <span class="token keyword">do</span> <span class="token builtin class-name">echo</span> <span class="token string">"<span class="token variable">$resource</span>"</span><span class="token punctuation">;</span> <span class="token keyword">done</span>
<span class="token punctuation">}</span>
<span class="token comment"># Execution starts here</span>
<span class="token builtin class-name">mapfile</span> <span class="token parameter variable">-t</span> resources <span class="token operator">&lt;</span> <span class="token operator">&lt;</span><span class="token punctuation">(</span>get_resources <span class="token string">"main.bicep"</span> <span class="token operator">|</span> <span class="token function">sort</span> -u<span class="token punctuation">)</span>
<span class="token builtin class-name">mapfile</span> <span class="token parameter variable">-t</span> locations <span class="token operator">&lt;</span> <span class="token operator">&lt;</span><span class="token punctuation">(</span>az account list-locations <span class="token parameter variable">--query</span> <span class="token string">"[].displayName"</span> <span class="token punctuation">\</span>
<span class="token parameter variable">--out</span> tsv<span class="token punctuation">)</span>
<span class="token keyword">for</span> <span class="token for-or-select variable">resource</span> <span class="token keyword">in</span> <span class="token string">"<span class="token variable">${resources<span class="token punctuation">[</span>@<span class="token punctuation">]</span>}</span>"</span>
<span class="token keyword">do</span>
<span class="token assign-left variable">namespace</span><span class="token operator">=</span><span class="token variable"><span class="token variable">$(</span><span class="token builtin class-name">echo</span> <span class="token string">"<span class="token variable">$resource</span>"</span> <span class="token operator">|</span> <span class="token function">cut</span> <span class="token parameter variable">-d</span> <span class="token string">"/"</span> <span class="token parameter variable">-f</span> <span class="token number">1</span> -<span class="token variable">)</span></span>
<span class="token assign-left variable">resourceType</span><span class="token operator">=</span><span class="token variable"><span class="token variable">$(</span><span class="token builtin class-name">echo</span> <span class="token string">"<span class="token variable">$resource</span>"</span> <span class="token operator">|</span> <span class="token function">cut</span> <span class="token parameter variable">-d</span> <span class="token string">"/"</span> <span class="token parameter variable">-f</span> <span class="token number">2</span> -<span class="token variable">)</span></span>
<span class="token builtin class-name">mapfile</span> <span class="token parameter variable">-t</span> newLocations <span class="token operator">&lt;</span> <span class="token operator">&lt;</span><span class="token punctuation">(</span>az provider show <span class="token parameter variable">--namespace</span> <span class="token string">"<span class="token variable">$namespace</span>"</span> <span class="token punctuation">\</span>
<span class="token parameter variable">--query</span> <span class="token string">"resourceTypes[?resourceType=='<span class="token variable">$resourceType</span>'].locations | [0]"</span> <span class="token punctuation">\</span>
<span class="token parameter variable">--out</span> tsv<span class="token punctuation">)</span>
<span class="token keyword">if</span> <span class="token punctuation">[</span><span class="token punctuation">[</span> <span class="token variable">${<span class="token operator">#</span>newLocations<span class="token punctuation">[</span>@<span class="token punctuation">]</span>}</span> <span class="token parameter variable">-eq</span> <span class="token number">0</span> <span class="token punctuation">]</span><span class="token punctuation">]</span>
<span class="token keyword">then</span>
<span class="token builtin class-name">continue</span>
<span class="token keyword">fi</span>
<span class="token builtin class-name">mapfile</span> <span class="token parameter variable">-t</span> locations <span class="token operator">&lt;</span> <span class="token operator">&lt;</span><span class="token punctuation">(</span><span class="token function">comm</span> <span class="token parameter variable">-12</span> <span class="token punctuation">\</span>
<span class="token operator">&lt;</span><span class="token punctuation">(</span><span class="token keyword">for</span> <span class="token for-or-select variable">location</span> <span class="token keyword">in</span> <span class="token string">"<span class="token variable">${locations<span class="token punctuation">[</span>@<span class="token punctuation">]</span>}</span>"</span><span class="token punctuation">;</span> <span class="token keyword">do</span> <span class="token builtin class-name">echo</span> <span class="token string">"<span class="token variable">$location</span>"</span><span class="token punctuation">;</span> <span class="token keyword">done</span><span class="token punctuation">)</span> <span class="token punctuation">\</span>
<span class="token operator">&lt;</span><span class="token punctuation">(</span><span class="token keyword">for</span> <span class="token for-or-select variable">location</span> <span class="token keyword">in</span> <span class="token string">"<span class="token variable">${newLocations<span class="token punctuation">[</span>@<span class="token punctuation">]</span>}</span>"</span><span class="token punctuation">;</span> <span class="token keyword">do</span> <span class="token builtin class-name">echo</span> <span class="token string">"<span class="token variable">$location</span>"</span><span class="token punctuation">;</span> <span class="token keyword">done</span><span class="token punctuation">)</span> <span class="token punctuation">)</span>
<span class="token keyword">done</span>
<span class="token keyword">for</span> <span class="token for-or-select variable">location</span> <span class="token keyword">in</span> <span class="token string">"<span class="token variable">${locations<span class="token punctuation">[</span>@<span class="token punctuation">]</span>}</span>"</span><span class="token punctuation">;</span> <span class="token keyword">do</span> <span class="token builtin class-name">echo</span> <span class="token string">"<span class="token variable">$location</span>"</span><span class="token punctuation">;</span> <span class="token keyword">done</span> <span class="token operator">|</span> <span class="token function">tee</span> locations.txt</code></pre>
</article>
<nav aria-label="pagination">
<ol class="pagination post-pagination">
<li class="older">
<a href="/fix-your-hearts/">
<i class="fa-solid fa-hand-point-left" aria-hidden="true"></i> fix your hearts
</a>
</li>
<li class="newer">
<a href="/redirections/">
redirections <i class="fa-solid fa-hand-point-right" aria-hidden="true"></i>
</a>
</li>
</ol>
</nav>
<hr>
<section class="related-posts">
<h2 id="related-posts">related posts</h2>
<ol id="postlist">
<li class="post">
<a class="postlink" href="/textures-unite/">
<img src="/img/textures-unite.jpg" alt="a largely unseen person holds up an expansive knitted shawl, built in 6 sections of different textures and colors. in the background, trees and dappled sunlight." loading="lazy" decoding="async" width="1000" height="1334">
<h2 id="textures-unite">textures unite</h2>
<ul class="postlist-tags">
<li>knit</li>
<li>highlight</li>
</ul>
</a>
</li>
<li class="post">
<a class="postlink" href="/designing-a-bag/">
<img src="/img/shoelace-bag.jpg" alt="a 3-image collage showcasing a leather crossbody bag. the leather body is brown and fairly simple. up the narrow sides, rope is laced through grommets in a style resembling a shoe lacing. the rope forms the handle and loops seamlessly through the other side of the bag, joining in one point in a figure-8 follow-through knot. At the bottom corners, there are small diagonal lines of stitching to give the bag a small lip around the base and ensure small objects don&#39;t slide out." loading="lazy" decoding="async" width="1000" height="1777">
<h2 id="designing-a-bag">designing a bag</h2>
<ul class="postlist-tags">
<li>leather</li>
<li>software</li>
</ul>
</a>
</li>
<li class="post">
<a class="postlink" href="/kestrel-zine/">
<img src="/img/kestrel-zine.jpg" alt="A 5 photo collage showing the front and back cover as well as 3 full spreads of a folded zine about Kestrel, my dog, who is a 65lb Malinois with a goofy smile and floppy ears. it is printed in two layers of color, blue and orange, and each image depicts Kestrel in various posts... alert and watchful, resting, looking mopey, wearing a sweatshirt." loading="lazy" decoding="async" width="1000" height="1000">
<h2 id="kestrel-zine">kestrel zine</h2>
<ul class="postlist-tags">
<li>print</li>
<li>zine</li>
<li>highlight</li>
</ul>
</a>
</li>
</ol>
</section>
</heading-anchors>
</main>
<footer>
<ul>
<li>
<a href="/colophon/">
colophon
</a>
</li>
<li>
<a href="/" title="go home" aria-label="go home | hello hello from Lee Cattarin in 2026">
hello hello from Lee Cattarin in 2026</a>
</li>
<li>
<a href="https://heckin.technology/inherentlee/leecat.art" title="source code" aria-label="source code" target="_blank" rel="external">
src
</a>
</li>
</ul>
</footer>
<!-- This page `/azure-locations-and-file-crawling/` was built on 2026-02-20T20:23:43.770Z -->
<body>
</body></body></html>