<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
	<channel>
		<title>Winterdom</title>
		<description>by dæmons be driven - a site by Tomas Restrepo</description>
		<link>https://winterdom.com/</link>
		<atom:link href="https://winterdom.com/feed.xml" rel="self" type="application/rss+xml" />
		
			<item>
				<title>Kondex: Teaching Claude to use it</title>
				<description>&lt;p&gt;In my previous two posts, I covered &lt;a href=&#34;https://winterdom.com/2026/09/14/kondex-a-declaration-indexing-tool&#34;&gt;how &lt;code&gt;kondex&lt;/code&gt; builds its index&lt;/a&gt; and &lt;a href=&#34;https://winterdom.com/2026/09/15/kondex-searching-the-index&#34;&gt;how you can query it&lt;/a&gt;. There&amp;rsquo;s one part I haven&amp;rsquo;t talked about yet, though: an index is not very useful if the LLM never thinks of using it. In this post, I&amp;rsquo;d like to go over how I got Claude Code to actually reach for &lt;code&gt;kondex&lt;/code&gt;, which turned out to be surprisingly hard, and what I learned along the way.&lt;/p&gt;&#xA;&lt;p&gt;If you look back at the architecture diagram in the first post, you&amp;rsquo;ll notice a box labeled &amp;ldquo;kondex plugin&amp;rdquo;, which contains a &lt;code&gt;SessionStart&lt;/code&gt; hook and a skill. Let&amp;rsquo;s see why both of them are there.&lt;/p&gt;&#xA;&lt;h2 id=&#34;starting-with-a-skill&#34;&gt;Starting with a skill&lt;/h2&gt;&#xA;&lt;p&gt;Claude Code supports &lt;a href=&#34;https://docs.claude.com/en/docs/claude-code/skills&#34;&gt;skills&lt;/a&gt;, which are just a folder with a &lt;code&gt;SKILL.md&lt;/code&gt; file describing what the skill is for and how to use it. Claude sees a list of the available skills (only their names and a short description), and loads the full contents of one when it decides it is relevant to the task at hand.&lt;/p&gt;&#xA;&lt;p&gt;Creating a skill was the obvious thing to try, so the first thing I did was write a skill, originally just called &lt;code&gt;kondex&lt;/code&gt;, with a full reference on how to use the tool: the commands, the ref syntax, the flags, and so on. That didn&amp;rsquo;t work, however. Claude kept doing what it always does, grepping through the repository and reading whole files.&lt;/p&gt;&#xA;&lt;!-- TODO: was the name your first suspect? Reword if the rename happened for a different reason. --&gt;&#xA;&lt;p&gt;This led me to wonder if the skill name was just too abstract. Yes, the skill description is obviously important, but one would guess a name like &lt;code&gt;kondex&lt;/code&gt; does not say anything about what the tool does. So I renamed the skill to &lt;code&gt;find-declarations&lt;/code&gt;, and rewrote the description so that it started with the questions it could answer. Unfortunately, that did not seem to make much difference either.&lt;/p&gt;&#xA;&lt;h2 id=&#34;measuring-it&#34;&gt;Measuring it&lt;/h2&gt;&#xA;&lt;p&gt;At this point, I decided to stop guessing and measure what was going on. The setup was fairly simple:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;A fixed question with a known answer, run against our monorepo. I picked one that &lt;code&gt;kondex&lt;/code&gt; is very good at answering: which columns of one of our connectors can be filtered server-side?&lt;/li&gt;&#xA;&lt;li&gt;A headless &lt;code&gt;claude -p&lt;/code&gt; run for each configuration I wanted to test. Each configuration differed from the others by exactly one command line flag (&lt;code&gt;--plugin-dir&lt;/code&gt;, &lt;code&gt;--append-system-prompt&lt;/code&gt;, or an environment variable).&lt;/li&gt;&#xA;&lt;li&gt;Counting the &lt;code&gt;kondex&lt;/code&gt; calls, greps and total tool calls in each session transcript.&lt;/li&gt;&#xA;&lt;li&gt;Three runs for each configuration, since the model is not deterministic and a single run doesn&amp;rsquo;t prove much.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Here are the results, averaged over the three runs:&lt;/p&gt;&#xA;&lt;table&gt;&#xA;&#x9;&lt;thead&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th&gt;What was installed&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th&gt;Runs that used kondex&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th&gt;kondex calls&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th&gt;Greps&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th&gt;Tool calls&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&lt;/thead&gt;&#xA;&#x9;&lt;tbody&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;Nothing&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;0/3&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;0.0&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;8.3&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;12.3&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;The skill&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;0/3&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;0.0&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;7.7&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;9.7&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;Skill + usage notes in the system prompt&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;3/3&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;7.7&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;0.0&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;8.0&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;Skill + &lt;code&gt;PreToolUse&lt;/code&gt; hook (advisory)&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;3/3&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;5.0&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;3.7&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;11.0&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;Skill + &lt;code&gt;PreToolUse&lt;/code&gt; hook (blocking)&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;3/3&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;7.3&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;3.0&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;14.3&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;Skill + usage notes from a &lt;code&gt;SessionStart&lt;/code&gt; hook&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;3/3&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;5.0&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;0.7&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;6.0&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;p&gt;The first two rows are the interesting ones. A session with the skill installed used &lt;code&gt;kondex&lt;/code&gt; exactly as often as a session with nothing installed at all, which was never. And the question was not a tricky one either: it was taken almost word for word from the skill&amp;rsquo;s own description.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Every configuration got the right answer, including the one with nothing installed. Claude can find the answer by grepping, it just takes longer and uses more context doing it. So this experiment was strictly about whether Claude used the tool or not.&lt;/p&gt;&#xA;&lt;h2 id=&#34;why-didnt-the-skill-work&#34;&gt;Why didn&amp;rsquo;t the skill work?&lt;/h2&gt;&#xA;&lt;p&gt;The best explanation I have is that it&amp;rsquo;s a matter of timing. Claude chooses skills from that list of names and descriptions, but it is not looking at the list when it decides to grep for something. That usually happens many tool calls into a task, in the middle of tracking something down, and not at the start, when it is deciding how to approach the problem. So no amount of rewording the description was going to help.&lt;/p&gt;&#xA;&lt;p&gt;On top of this, I would presume that built-in tools probably just take precedence over whatever skills describe.&lt;/p&gt;&#xA;&lt;p&gt;What does work is having the guidance in context before the first search. You can see this in the third row of the table: putting the same usage notes directly in the system prompt got &lt;code&gt;kondex&lt;/code&gt; used in every single run, and the greps went away entirely.&lt;/p&gt;&#xA;&lt;p&gt;The two attempts using &lt;code&gt;PreToolUse&lt;/code&gt; are also interesting. A &lt;code&gt;PreToolUse&lt;/code&gt; hook runs before each tool call, so I wrote one that caught greps that looked like a search for a declaration, and suggested (or required) using &lt;code&gt;kondex&lt;/code&gt; instead. This does get &lt;code&gt;kondex&lt;/code&gt; used, but the hook only fires after Claude has already decided to grep, so you pay for the abandoned attempt: 11 to 14 tool calls, which is worse than having nothing installed at all. A good attempt, but ended up having to scrap it.&lt;/p&gt;&#xA;&lt;h2 id=&#34;why-a-plugin&#34;&gt;Why a plugin?&lt;/h2&gt;&#xA;&lt;p&gt;So the guidance needs to be in context right from the start of the session. There are a few ways to do that, but each of them has a problem:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;&lt;code&gt;--append-system-prompt&lt;/code&gt;&lt;/strong&gt;: This works, but nobody is going to type that every time they start Claude Code.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;A &lt;code&gt;CLAUDE.md&lt;/code&gt; file&lt;/strong&gt;: Adding the instructions to the repository would also work, but that means committing &lt;code&gt;kondex&lt;/code&gt;-specific instructions into every repository that uses it. For our monorepo, that means a pull request against a code base shared by the whole team, for a tool that not everyone has installed.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;A &lt;code&gt;SessionStart&lt;/code&gt; hook&lt;/strong&gt;: This runs when a session starts, and can add text to Claude&amp;rsquo;s context, which is exactly what we need. Hooks can also be distributed as part of a &lt;a href=&#34;https://docs.claude.com/en/docs/claude-code/plugins&#34;&gt;Claude Code plugin&lt;/a&gt;.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;That&amp;rsquo;s the last row of the table, and it had the best results of all: &lt;code&gt;kondex&lt;/code&gt; got used in every run, there were almost no greps, and Claude needed half the tool calls it used with nothing installed, because it skipped the exploratory grepping altogether.&lt;/p&gt;&#xA;&lt;h2 id=&#34;whats-in-the-plugin&#34;&gt;What&amp;rsquo;s in the plugin&lt;/h2&gt;&#xA;&lt;p&gt;The plugin lives in the &lt;code&gt;kondex&lt;/code&gt; repository itself, and it&amp;rsquo;s pretty small:&lt;/p&gt;&#xA;&lt;div class=&#34;codeblock&#34;&gt;&#xA;  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;.claude-plugin/&#xA;  marketplace.json          makes the repository installable as a marketplace&#xA;claude/&#xA;  .claude-plugin/&#xA;    plugin.json             the plugin manifest&#xA;  hooks/&#xA;    hooks.json              registers the SessionStart hook&#xA;    session_context.py      the hook itself&#xA;  fragment.md               the usage notes the hook puts in context&#xA;  skills/&#xA;    find-declarations/&#xA;      SKILL.md              the full reference&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;/div&gt;&#xA;&lt;p&gt;Installing it only takes a couple of commands inside Claude Code:&lt;/p&gt;&#xA;&lt;div class=&#34;codeblock&#34;&gt;&#xA;  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;/plugin marketplace add &amp;lt;path-to-kondex&amp;gt;&#xA;/plugin install kondex&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;/div&gt;&#xA;&lt;p&gt;Or, if you just want to try it for a single session:&lt;/p&gt;&#xA;&lt;div class=&#34;codeblock&#34;&gt;&#xA;  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt; claude --plugin-dir &amp;lt;path-to-kondex&amp;gt;\claude&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;/div&gt;&#xA;&lt;p&gt;The hook is registered with a few lines of JSON:&lt;/p&gt;&#xA;&lt;div class=&#34;codeblock&#34;&gt;&#xA;  &lt;div class=&#34;codeblock__bar&#34;&gt;&#xA;    &lt;span class=&#34;codeblock__name&#34;&gt;json&lt;/span&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;#34;hooks&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;SessionStart&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;hooks&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nt&#34;&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;command&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nt&#34;&gt;&amp;#34;command&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;python \&amp;#34;${CLAUDE_PLUGIN_ROOT}/hooks/session_context.py\&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nt&#34;&gt;&amp;#34;timeout&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;20&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&lt;/div&gt;&#xA;&lt;p&gt;The &lt;code&gt;session_context.py&lt;/code&gt; script doesn&amp;rsquo;t do much more than that:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;It runs &lt;code&gt;kondex status&lt;/code&gt; to check whether the session is starting inside a git repository that &lt;code&gt;kondex&lt;/code&gt; can work with. If &lt;code&gt;kondex&lt;/code&gt; is not installed, or this isn&amp;rsquo;t a git repository, it returns nothing, so a session somewhere else doesn&amp;rsquo;t pay for anything. In my case, the tool will always be present, but it didn&amp;rsquo;t hurt to add just in case.&lt;/li&gt;&#xA;&lt;li&gt;Otherwise, it reads &lt;code&gt;fragment.md&lt;/code&gt; and returns it to Claude Code as &lt;code&gt;additionalContext&lt;/code&gt;.&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;Using &lt;code&gt;kondex status&lt;/code&gt; for the check was on purpose: it is the only command that reports on a repository without indexing it, so it&amp;rsquo;s safe to run at the start of every session.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Make sure you use the &lt;code&gt;additionalContext&lt;/code&gt; field. A hook can also return a &lt;code&gt;systemMessage&lt;/code&gt;, but that is only shown to the user in the console, and never reaches the model.&lt;/p&gt;&#xA;&lt;p&gt;If you want to turn the hook off without uninstalling the plugin, you can set the &lt;code&gt;KONDEX_SESSION_CONTEXT&lt;/code&gt; environment variable to &lt;code&gt;off&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;The downside:&lt;/strong&gt;: &lt;code&gt;kondex&lt;/code&gt; supports only a specific subset of languages. Having the plugin enabled when working on a repo that uses an unsupported language would probably not&amp;hellip; be a great user experience. Something I might look into improving later on.&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-fragment&#34;&gt;The fragment&lt;/h2&gt;&#xA;&lt;p&gt;The &lt;code&gt;fragment.md&lt;/code&gt; file contains the text Claude actually sees at the start of every session. It&amp;rsquo;s about 150 lines long, and it starts like this:&lt;/p&gt;&#xA;&lt;div class=&#34;codeblock&#34;&gt;&#xA;  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;## Finding declarations&#xA;&#xA;This repository is indexed by `kondex`. **Use it before grepping for a class&#xA;name or opening a source file to look up a declaration.** A query costs ~0.5s&#xA;and a few hundred tokens; reading a 4,000-line file to find one signature costs&#xA;~50,000.&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;/div&gt;&#xA;&lt;p&gt;After that comes a short cheat sheet with every command and its most useful flags, followed by a few paragraphs covering the things Claude kept getting wrong.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; This isn&amp;rsquo;t free. The fragment adds roughly 2,300 tokens to the context of every session in a repository. Considering that a single read of a large source file can cost 20 times that, I think it&amp;rsquo;s a good trade, but it&amp;rsquo;s something to be aware of.&lt;/p&gt;&#xA;&lt;p&gt;The skill is still part of the plugin, but it&amp;rsquo;s now more of a fallback. In the configurations that used the fragment, the skill was only loaded zero or one times out of three, because the fragment already had what Claude needed. A skill costs nothing until it&amp;rsquo;s used, so it is still a good place for the full reference that doesn&amp;rsquo;t fit in the fragment.&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;Something I really wished Claude Code had better support for was reporting metrics for plugin usage. Unfortunately, the OpenTelemetry-based instrumentation in Claude Code is useless unless your plugin comes from a &amp;ldquo;known marketplace&amp;rdquo;. Sigh.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;h2 id=&#34;trying-it-on-real-work&#34;&gt;Trying it on real work&lt;/h2&gt;&#xA;&lt;p&gt;The results in the table come from a single question, on a single repository, with three runs for each configuration. That&amp;rsquo;s a good start, but I wanted to see what would happen on real work. So I took an actual ticket from our backlog and had Claude Code investigate and fix it twice: once with only the skill installed, and once with the plugin.&lt;/p&gt;&#xA;&lt;p&gt;The good news is things worked as expected: The fragment injected by the hook did what it was supposed to do, and Claude went from never using &lt;code&gt;kondex&lt;/code&gt; to calling it 10 times, the first one before it did any grepping.&lt;/p&gt;&#xA;&lt;p&gt;Unfortunately (and it was surprising), it kept grepping just as much as before. Both sessions ran 32 searches over the source code, and used about the same number of tool calls and output tokens. Even the time was almost the same (49 minutes against 48). So the &amp;ldquo;half the tool calls&amp;rdquo; result from the table did not carry over to a real investigate-and-fix task at all.&lt;/p&gt;&#xA;&lt;p&gt;Claude Code is fantastic at researching its own session transcripts, so of course I had it go through the transcripts for these attempts. I found three reasons for this:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;About half of the remaining greps were looking for references rather than declarations: who calls this method, or where is this helper used. &lt;code&gt;kondex&lt;/code&gt; only indexes declarations, so no prompt was going to fix that. (This is also what eventually led to the experimental &lt;code&gt;callers&lt;/code&gt; command I skipped in the last post.)&lt;/li&gt;&#xA;&lt;li&gt;The fragment was missing two flags: &lt;code&gt;--sig&lt;/code&gt; and &lt;code&gt;--limit&lt;/code&gt;. Twice, Claude was using &lt;code&gt;kondex&lt;/code&gt; correctly, then hit the default limit of 30 results, or couldn&amp;rsquo;t see the declaration it needed, and went back to grep. Both flags were documented in the skill, but since the skill was never loaded, Claude never saw them.&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;kondex&lt;/code&gt; indexes the content staged in git. While Claude is editing files during the fix, those files are stale in the index by design, so grep is actually the right tool for them.&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;After adding the missing flags to the fragment, I tried again with a different ticket. This time Claude ran 13 greps instead of 32, and the whole session took 23 minutes instead of 49. It&amp;rsquo;s a single run on a different task, so I wouldn&amp;rsquo;t read too much into the exact numbers, but it was a nice step in the right direction.&lt;/p&gt;&#xA;&lt;h2 id=&#34;designing-the-output&#34;&gt;Designing the output&lt;/h2&gt;&#xA;&lt;p&gt;Once Claude was using the tool regularly, it also became clear that the output of &lt;code&gt;kondex&lt;/code&gt; is part of the prompt too (or rather part of the model input tokens), and it needs to be designed with that in mind. Here are a couple of examples from those same sessions:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;Counting results&lt;/strong&gt;: In one session, Claude asked for every implementation of a very common method. &lt;code&gt;kondex&lt;/code&gt; correctly returned 180 results, but when a tool result is that large, Claude Code saves it to a file and only keeps a preview of the beginning in context. The total count was at the end of the output, so it got cut off, and Claude&amp;rsquo;s final report said there were around 30 implementations, when the real number was 145. This is why long listings now show the total count at the top as well, and why the &lt;code&gt;--count&lt;/code&gt; flag exists.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Using line ranges&lt;/strong&gt;: Every result from &lt;code&gt;kondex&lt;/code&gt; includes a line range, and the fragment told Claude to use it to read only the lines it needed. Claude did use the start line, but across 19 reads, it used the end line only once. In one case, it read the same file six times in overlapping chunks, 28.7 KB in total, to get to a method that was only 944 bytes long. The problem is that the &lt;code&gt;Read&lt;/code&gt; tool takes a starting line and a number of lines rather than a start and an end, and that bit of arithmetic was enough to lose it. Rewording the instructions wouldn&amp;rsquo;t have helped much here, but adding &lt;code&gt;show --body&lt;/code&gt; did have a significant impact.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;&#xA;&lt;p&gt;The surprising conclusion from this was that the skill on its own turned out to not not be sufficient to get Claude to use &lt;code&gt;kondex&lt;/code&gt;, since Claude doesn&amp;rsquo;t look at the skill when it decides to grep for something. Adding the usage notes at the start of every session through a plugin hook is what finally made the difference, and it took lots of testing and careful measuring of each attempt to discover this.&lt;/p&gt;&#xA;</description>
				<pubDate>Wed, 16 Sep 2026 00:00:00 +0000</pubDate>
				<link>https://winterdom.com/2026/09/16/kondex-teaching-claude-to-use-it/</link>
				<guid isPermaLink="true">https://winterdom.com/2026/09/16/kondex-teaching-claude-to-use-it/</guid>
			</item>
		
			<item>
				<title>Kondex: Searching the index</title>
				<description>&lt;p&gt;In my &lt;a href=&#34;https://winterdom.com/2026/09/14/kondex-a-declaration-indexing-tool&#34;&gt;last post&lt;/a&gt;, I introduced &lt;code&gt;kondex&lt;/code&gt;, a declaration indexing tool I wrote for our team. Today, I&amp;rsquo;d like to spend some time going over how to use the index to navigate the code base.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; &lt;code&gt;kondex&lt;/code&gt; will automatically detect a stale the index on any command and do an incremental scan if necessary.&lt;/p&gt;&#xA;&lt;h2 id=&#34;search&#34;&gt;Search&lt;/h2&gt;&#xA;&lt;p&gt;The first entry point to the index is the &lt;code&gt;search&lt;/code&gt; command, which uses a case-insensitive substring match on the declaration name and finds matching declarations:&lt;/p&gt;&#xA;&lt;div class=&#34;codeblock&#34;&gt;&#xA;  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt; kondex search OnViewportWidthChanged&#xA;method  Winterdom.Viasfora.Rainbow.RainbowLines#OnViewportWidthChanged(object,EventArgs)  src/Viasfora.Rainbow/RainbowLines.cs:154-157&#xA;method  Winterdom.Viasfora.Text.CurrentLineAdornment#OnViewportWidthChanged(object,EventArgs)  src/Viasfora.Core/Text/CurrentLineAdornment.cs:83-85&#xA;method  Winterdom.Viasfora.Text.PresentationMode#OnViewportWidthChanged(object,EventArgs)  src/Viasfora.Core/Text/PresentationMode.cs:28-31&#xA;method  Winterdom.Viasfora.Util.ToolTipWindow#OnViewportWidthChanged(object,EventArgs)  src/Viasfora.Rainbow/Util/ToolTipWindow.cs:76-81&#xA;&#xA;4 results&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;/div&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The result of a search in &lt;code&gt;kondex&lt;/code&gt; lists what we call a &lt;code&gt;ref&lt;/code&gt;, which will usually take the form of &lt;code&gt;Type#member(parameter types)&lt;/code&gt;, with constructors using the &lt;code&gt;&amp;lt;init&amp;gt;&lt;/code&gt; name.&lt;/p&gt;&#xA;&lt;p&gt;Members declared outside of a type will be owned by the module/package they are declared in:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Go functions will be owned by their package (e.g. &lt;code&gt;scan#Run()&lt;/code&gt;)&lt;/li&gt;&#xA;&lt;li&gt;Python / TypeScript functions at module level will have an empty owner (e.g. &lt;code&gt;#UseThis()&lt;/code&gt;)&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;When searching, exact names will rank first, then prefixes, then any other matches. A term containing &lt;code&gt;.&lt;/code&gt;, &lt;code&gt;#&lt;/code&gt; or &lt;code&gt;(&lt;/code&gt; is matched against the full ref rather than the name, so &lt;code&gt;search &#39;Rainbow#On&#39;&lt;/code&gt; works.&lt;/p&gt;&#xA;&lt;p&gt;There are a couple of interesting additional filters that can be used for search. First, you can narrow down results to only declarations of a specific kind:&lt;/p&gt;&#xA;&lt;div class=&#34;codeblock&#34;&gt;&#xA;  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt; kondex search --kind class Rainbow&#xA;30 results for &amp;#34;Rainbow&amp;#34; (kind class); more matched&#xA;&#xA;class  Winterdom.Viasfora.Rainbow.Rainbows                          src/Viasfora.Rainbow/Rainbows.cs:4-19&#xA;class  Winterdom.Viasfora.Tags.RainbowTag                           src/Viasfora.Core/Tags/RainbowTag.cs:6-11&#xA;class  Winterdom.Viasfora.Rainbow.RainbowLines                      src/Viasfora.Rainbow/RainbowLines.cs:52-359&#xA;...&#xA;class  Winterdom.Viasfora.Rainbow.RainbowKeyProcessorProvider       src/Viasfora.Rainbow/RainbowKeyProcessor.cs:11-24&#xA;&#xA;30 results shown; more matched — raise --limit, or narrow with --kind/--path&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;/div&gt;&#xA;&lt;p&gt;Note that in the previous example, &lt;code&gt;kondex&lt;/code&gt; found over 30 matches, so the result is partial. You can do a &lt;code&gt;search --count&lt;/code&gt; to get just the total number of results without any extra details.&lt;/p&gt;&#xA;&lt;p&gt;You can also limit results by only searching within a specified path:&lt;/p&gt;&#xA;&lt;div class=&#34;codeblock&#34;&gt;&#xA;  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt; kondex search Rainbow --path .\src\Viasfora.Core\&#xA;class        Winterdom.Viasfora.Tags.RainbowTag                              src/Viasfora.Core/Tags/RainbowTag.cs:6-11&#xA;constructor  Winterdom.Viasfora.Tags.RainbowTag#&amp;lt;init&amp;gt;(IClassificationType)  src/Viasfora.Core/Tags/RainbowTag.cs:8-10&#xA;field        Winterdom.Viasfora.Guids#RainbowOptions                         src/Viasfora.Core/Guids.cs:9&#xA;field        Winterdom.Viasfora.PkgCmdIdList#cmdidRainbowNext                src/Viasfora.Core/PkgCmdIdList.cs:15&#xA;field        Winterdom.Viasfora.PkgCmdIdList#cmdidRainbowPrevious            src/Viasfora.Core/PkgCmdIdList.cs:14&#xA;&#xA;5 results&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;/div&gt;&#xA;&lt;h2 id=&#34;outline&#34;&gt;Outline&lt;/h2&gt;&#xA;&lt;p&gt;Another extremely useful feature of &lt;code&gt;kondex&lt;/code&gt; is being able to return an outline for a file (meaning, a simplified view of the declarations in the file). Again, this saves LLM tools from having to read the file and extract them directly:&lt;/p&gt;&#xA;&lt;div class=&#34;codeblock&#34;&gt;&#xA;  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt; kondex outline .\src\Viasfora.Languages\Sql.cs&#xA;src/Viasfora.Languages/Sql.cs  (11 declarations)&#xA;&#xA;  class        Sql                             8-25&#xA;  field          #knownContentTypes            10-11&#xA;  property       #SupportedContentTypes        12&#xA;  property       #Settings                     13&#xA;  constructor    #&amp;lt;init&amp;gt;(ITypedSettingsStore)  15-21&#xA;  method         #NewBraceScanner()            23-24&#xA;  class        SqlSettings                     27-43&#xA;  property       #ControlFlowDefaults          28-32&#xA;  property       #LinqDefaults                 33-35&#xA;  property       #VisibilityDefaults           36-38&#xA;  constructor    #&amp;lt;init&amp;gt;(ITypedSettingsStore)  40-42&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;/div&gt;&#xA;&lt;p&gt;The &lt;code&gt;outline&lt;/code&gt; command also accepts a folder path as an input, in which case it will produce an outline of all files in the directory (recursively). Since this could be a very large result, it will only report a number of findings (200 declarations by default). You can use &lt;code&gt;--limit&lt;/code&gt; and &lt;code&gt;--offset&lt;/code&gt; to narrow it down.&lt;/p&gt;&#xA;&lt;h2 id=&#34;members&#34;&gt;Members&lt;/h2&gt;&#xA;&lt;p&gt;One common question an LLM might want to answer is: What members does this type declare? That&amp;rsquo;s what the &lt;code&gt;members&lt;/code&gt; command is for:&lt;/p&gt;&#xA;&lt;div class=&#34;codeblock&#34;&gt;&#xA;  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt; kondex members Winterdom.Viasfora.Rainbow.CharPos&#xA;struct Winterdom.Viasfora.Rainbow.CharPos  src/Viasfora.Languages/CharPos.cs&#xA;  public struct CharPos&#xA;&#xA;  field        CharPos#ch                    5&#xA;               private readonly char ch&#xA;  field        CharPos#state                 6&#xA;               private readonly int state&#xA;  field        CharPos#position              7&#xA;               private readonly int position&#xA;  field        CharPos#Empty                 8&#xA;               public static CharPos Empty&#xA;  property     CharPos#Char                  10&#xA;               public char Char&#xA;  property     CharPos#State                 11&#xA;               public int State&#xA;  property     CharPos#Position              12&#xA;               public int Position&#xA;  constructor  CharPos#&amp;lt;init&amp;gt;(char,int)      14-15&#xA;               public CharPos(char ch, int pos) : this(ch, pos, 0)&#xA;  constructor  CharPos#&amp;lt;init&amp;gt;(char,int,int)  17-21&#xA;               public CharPos(char ch, int pos, int state)&#xA;  method       CharPos#ToString()            27-29&#xA;               public override string ToString()&#xA;&#xA;10 members&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;/div&gt;&#xA;&lt;p&gt;There are two options that can be very useful to &lt;code&gt;members&lt;/code&gt;:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;The &lt;code&gt;--public&lt;/code&gt; flag will ask &lt;code&gt;kondex&lt;/code&gt; to only list public members of the specific class. The rules for each language will determine what is considered public:&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Java / C# includes only strict &lt;code&gt;public&lt;/code&gt; members (not &lt;code&gt;protected&lt;/code&gt;, &lt;code&gt;internal&lt;/code&gt;, or &lt;code&gt;package&lt;/code&gt;)&lt;/li&gt;&#xA;&lt;li&gt;Go will define it based on capitalization&lt;/li&gt;&#xA;&lt;li&gt;TypeScript will define it based on &lt;code&gt;export&lt;/code&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;The &lt;code&gt;--inherited&lt;/code&gt; flag will ask &lt;code&gt;kondex&lt;/code&gt; to not only list members directly defined in the specified type, but also in the superclasses (or interfaces) in the source (it will obviously not return anything that comes from external, unindexed libraries). Since &lt;code&gt;kondex&lt;/code&gt; only does a basic lexical scan, the matching is done by name, which means this is supported strictly as a best-effort.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;show&#34;&gt;Show&lt;/h2&gt;&#xA;&lt;p&gt;Once you&amp;rsquo;ve found the declaration you are looking for, you can &lt;code&gt;show&lt;/code&gt; it:&lt;/p&gt;&#xA;&lt;div class=&#34;codeblock&#34;&gt;&#xA;  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt; kondex show &amp;#34;Winterdom.Viasfora.Util.ToolTipWindow#OnViewportWidthChanged(object,EventArgs)&amp;#34;&#xA;method Winterdom.Viasfora.Util.ToolTipWindow#OnViewportWidthChanged(object,EventArgs)&#xA;  file         src/Viasfora.Rainbow/Util/ToolTipWindow.cs:76-81&#xA;  declaration  private void OnViewportWidthChanged(object sender, EventArgs e)&#xA;  declared in  Winterdom.Viasfora.Util.ToolTipWindow&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;/div&gt;&#xA;&lt;p&gt;Note how this gives you the complete method declaration, along with a location (file + line range). This is the ideal case when all you care is about what the declaration looks like (for example, you just want the method signature)&lt;/p&gt;&#xA;&lt;p&gt;One interesting feature of &lt;code&gt;show&lt;/code&gt; is that &lt;code&gt;kondex&lt;/code&gt; is aware of language-specific syntaxes for documenting declarations, such as &lt;em&gt;javadoc&lt;/em&gt; comments or C#&amp;rsquo;s &lt;code&gt;///&lt;/code&gt; comments, and will return those as part of the &lt;code&gt;show&lt;/code&gt; command where available:&lt;/p&gt;&#xA;&lt;div class=&#34;codeblock&#34;&gt;&#xA;  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt; kondex show &amp;#34;Winterdom.Viasfora.Options.MainOptionsControl#Dispose&amp;#34;&#xA;method Winterdom.Viasfora.Options.MainOptionsControl#Dispose(bool)&#xA;  file         src/Viasfora/Options/MainOptionsControl.Designer.cs:12-17&#xA;  declaration  protected override void Dispose(bool disposing)&#xA;  declared in  Winterdom.Viasfora.Options.MainOptionsControl&#xA;&#xA;  Clean up any resources being used.&#xA;&#xA;  @param disposing true if managed resources should be disposed; otherwise, false.&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;/div&gt;&#xA;&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The above example is interesting in that it searches the declaration based on a partial prefix: The ref used is missing the parameter list!&lt;/p&gt;&#xA;&lt;p&gt;There are cases when an LLM might want more than a signature. For example, it might need to read what the method implementation looks like. In that case, &lt;code&gt;kondex show&lt;/code&gt; also has the &lt;code&gt;--body&lt;/code&gt; argument, which gives you its source or body:&lt;/p&gt;&#xA;&lt;div class=&#34;codeblock&#34;&gt;&#xA;  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt; kondex show &amp;#34;Winterdom.Viasfora.Util.ToolTipWindow#OnViewportWidthChanged(object,EventArgs)&amp;#34; --body&#xA;method Winterdom.Viasfora.Util.ToolTipWindow#OnViewportWidthChanged(object,EventArgs)&#xA;  file         src/Viasfora.Rainbow/Util/ToolTipWindow.cs:76-81&#xA;  declaration  private void OnViewportWidthChanged(object sender, EventArgs e)&#xA;  declared in  Winterdom.Viasfora.Util.ToolTipWindow&#xA;&#xA;  76      private void OnViewportWidthChanged(object sender, EventArgs e) {&#xA;  77        this.tipView.ViewportWidthChanged -= this.OnViewportWidthChanged;&#xA;  78        if ( this.tipView.ViewportRight &amp;gt; this.tipView.ViewportLeft ) {&#xA;  79          this.ScrollIntoView(this.pointToDisplay);&#xA;  80        }&#xA;  81      }&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;/div&gt;&#xA;&lt;p&gt;This is very useful to LLM tools as it saves them the need to open the file directly and scan the line range.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; In some cases, you might have multiple declarations with the same name. In that case, you will want to either specify &lt;code&gt;--path&lt;/code&gt; or &lt;code&gt;--kind&lt;/code&gt; to ensure you get the right one. Note that if the input to &lt;code&gt;show&lt;/code&gt; is ambiguous, it will provide a list to disambiguate:&lt;/p&gt;&#xA;&lt;div class=&#34;codeblock&#34;&gt;&#xA;  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt; kondex show OnViewportWidthChanged&#xA;&amp;#34;OnViewportWidthChanged&amp;#34; matches 4 declarations:&#xA;  method  Winterdom.Viasfora.Rainbow.RainbowLines#OnViewportWidthChanged(object,EventArgs)  src/Viasfora.Rainbow/RainbowLines.cs:154-157&#xA;  method  Winterdom.Viasfora.Text.CurrentLineAdornment#OnViewportWidthChanged(object,EventArgs)  src/Viasfora.Core/Text/CurrentLineAdornment.cs:83-85&#xA;  method  Winterdom.Viasfora.Text.PresentationMode#OnViewportWidthChanged(object,EventArgs)  src/Viasfora.Core/Text/PresentationMode.cs:28-31&#xA;  method  Winterdom.Viasfora.Util.ToolTipWindow#OnViewportWidthChanged(object,EventArgs)  src/Viasfora.Rainbow/Util/ToolTipWindow.cs:76-81&#xA;retry with one of the refs above, or narrow with --kind/--path&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;/div&gt;&#xA;&lt;h2 id=&#34;terraform&#34;&gt;Terraform&lt;/h2&gt;&#xA;&lt;p&gt;I thought including a short example of using &lt;code&gt;kondex&lt;/code&gt; on code bases including terraform scripts would be interesting, as the syntax used can be somewhat different from other languages. For example, searching:&lt;/p&gt;&#xA;&lt;div class=&#34;codeblock&#34;&gt;&#xA;  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt; kondex search dns&#xA;30 results for &amp;#34;dns&amp;#34;; more matched&#xA;&#xA;data_source  data.azurerm_resource_group.dns-rg                  terraform/dev/dns.tf:29-32&#xA;data_source  data.azurerm_resource_group.dns-rg                  terraform/modules/cdatakube_assets/external-dns.tf:5-8&#xA;data_source  data.azurerm_resource_group.dns-rg                  terraform/prod/security.tf:65-67&#xA;...&#xA;&#xA;30 results shown; more matched — raise --limit, or narrow with --kind/--path&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;/div&gt;&#xA;&lt;p&gt;You can see Terraform introduces its own set of declaration kinds (data_source, variable, resource, module, output, local, provider). Showing a declaration still works the same way:&lt;/p&gt;&#xA;&lt;div class=&#34;codeblock&#34;&gt;&#xA;  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt; kondex show azuread_application.azuredns-sp&#xA;resource azuread_application.azuredns-sp&#xA;  file         terraform/prod/security.tf:28-31&#xA;  declaration  resource &amp;#34;azuread_application&amp;#34; &amp;#34;azuredns-sp&amp;#34;&#xA;  members      none&#xA;&#xA;  Create Azure AD App for azure dns&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;/div&gt;&#xA;&lt;p&gt;An interesting aspect for Terraform is that refs use the exact same syntax the configuration language uses to refer to things.&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-workflow&#34;&gt;The workflow&lt;/h2&gt;&#xA;&lt;p&gt;By now, you can probably guess there&amp;rsquo;s an implicit workflow to using &lt;code&gt;kondex&lt;/code&gt;:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;search&lt;/code&gt; to find the name&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;outline&lt;/code&gt; or &lt;code&gt;members&lt;/code&gt; to see the shape&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;show&lt;/code&gt; when you want the complete declaration, &lt;code&gt;show --body&lt;/code&gt; when you need the implementation&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;In other words, the whole purpose is to provide a tool that helps an LLM narrow down as much as possible the possibilities before it reads anything.&lt;/p&gt;&#xA;&lt;p&gt;One thing worth being honest about: The examples above are almost all from the Viasfora code base, which, let&amp;rsquo;s face it, is very small. The numbers don&amp;rsquo;t reflect much complexity. However, the tool can have substantial impact when operating on a large code base, particularly one that has lots of larger source files. This is where &lt;code&gt;kondex&lt;/code&gt; really shines, as it lets the LLM avoid having to read larger files directly, or spend multiple cycles trying to figure out the exact boundaries of a declaration for a narrow read.&lt;/p&gt;&#xA;&lt;p&gt;In the next post, I will cover something that was hinted at in the original article, but not explicitly mentioned: Why &lt;code&gt;kondex&lt;/code&gt; comes with a Claude Code plugin!&lt;/p&gt;&#xA;</description>
				<pubDate>Tue, 15 Sep 2026 00:00:00 +0000</pubDate>
				<link>https://winterdom.com/2026/09/15/kondex-searching-the-index/</link>
				<guid isPermaLink="true">https://winterdom.com/2026/09/15/kondex-searching-the-index/</guid>
			</item>
		
			<item>
				<title>Kondex: A declaration indexing tool</title>
				<description>&lt;p&gt;It&amp;rsquo;s been a very long time since I&amp;rsquo;ve written on this blog, but wanted to pick this up again, and thought tools would be an excellent topic.&lt;/p&gt;&#xA;&lt;p&gt;Like most people in the Software Industry, I&amp;rsquo;ve been spending increasing amounts of time getting comfortable with LLM-based tools. This includes not only using tools such as GitHub Copilot and Claude Code, but also figuring out better ways to make our engineering team used to them and able to use them effectively.&lt;/p&gt;&#xA;&lt;p&gt;In the next few days, I&amp;rsquo;d like to cover some of the internal tools I&amp;rsquo;ve built to help support this journey.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Right now, these tools are purely internal and not published externally. However, if someone finds them useful, let me know and I&amp;rsquo;ll see what I can do.&lt;/p&gt;&#xA;&lt;p&gt;First, I&amp;rsquo;d like to cover a tool called &lt;code&gt;kondex&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;h2 id=&#34;what-is-kondex&#34;&gt;What is Kondex?&lt;/h2&gt;&#xA;&lt;p&gt;Kondex is a utility tool that aims to simplify how LLM tools navigate a code base. There&amp;rsquo;s plenty of tools out there that claim to do this by indexing the code itself and then providing search operations on top of the index.&lt;/p&gt;&#xA;&lt;p&gt;Kondex aims to solve the same issue, but from a slightly different perspective. Kondex does not apply any kind of RAG or vector-driven search. Instead, it works by building an index of all declarations (like classes, methods and other members), and then providing primitives to search through these declarations.&lt;/p&gt;&#xA;&lt;h2 id=&#34;why-build-it&#34;&gt;Why build it?&lt;/h2&gt;&#xA;&lt;p&gt;This is an excellent question. Like I said, there&amp;rsquo;s a number of similar tools out there (many open source), but after testing several of them, I couldn&amp;rsquo;t find one that would do exactly what I wanted and worked well with our specific requirements.&lt;/p&gt;&#xA;&lt;p&gt;The biggest issues I ran into with other tools were:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;Size of the code base:&lt;/strong&gt; Our connector team works off a fairly big Git monorepo from which we build and deliver 1000s of artifacts, and contains millions of lines of code. Many of the tools I tried would need well over 10 minutes to even do the initial indexing, and even incremental indexing wasn&amp;rsquo;t very performant.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Support for Git worktrees:&lt;/strong&gt; Our team relies heavily on using &lt;a href=&#34;https://git-scm.com/docs/git-worktree&#34;&gt;Git Worktrees&lt;/a&gt;. Most of the tools I tried are completely unaware of worktrees, and treat each separate branch on disk as a completely different repo, requiring generating a full index every time you created a new worktree. This was unacceptable for our use case.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Ignoring files:&lt;/strong&gt; Our team makes heavy use of code generation. Our build process not only generates a lot of code files, but also copies stuff around while doing builds, all of which goes into a &lt;code&gt;./Release&lt;/code&gt; folder that&amp;rsquo;s excluded by &lt;code&gt;.gitignore&lt;/code&gt;. The problem is most tools don&amp;rsquo;t know anything about this, and will happily index all source files on disk, polluting the index with either duplicate files or just generated code that&amp;rsquo;s irrelevant.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Solving these issues was the primary driver behind developing &lt;code&gt;kondex&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;h2 id=&#34;architecture&#34;&gt;Architecture&lt;/h2&gt;&#xA;&lt;p&gt;Kondex is implemented using &lt;a href=&#34;https://go.dev/&#34;&gt;Go&lt;/a&gt;. I wanted to use a language that would produce native tools for best performance. I am not an expert at Go, but that&amp;rsquo;s where &lt;a href=&#34;https://claude.com/product/claude-code&#34;&gt;Claude Code&lt;/a&gt; becomes very useful. It helped me build the initial working prototype fast and evolve it over time to fix issues and support new features. The following diagram shows the basic architecture:&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://static.winterdom.com/images/2026/kondex-architecture.svg&#34; alt=&#34;kondex architecture&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;Parsing source files is achieved through the use of &lt;a href=&#34;https://tree-sitter.github.io/tree-sitter/&#34;&gt;Tree Sitter&lt;/a&gt;, which does an excellent job of abstracting the complexity of building a syntax tree; &lt;code&gt;kondex&lt;/code&gt; uses this as the base for its declaration extraction engine. Currently, &lt;code&gt;kondex&lt;/code&gt; supports parsing declarations from the following languages:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Java&lt;/li&gt;&#xA;&lt;li&gt;Kotlin&lt;/li&gt;&#xA;&lt;li&gt;C#&lt;/li&gt;&#xA;&lt;li&gt;Go&lt;/li&gt;&#xA;&lt;li&gt;Python&lt;/li&gt;&#xA;&lt;li&gt;TypeScript&lt;/li&gt;&#xA;&lt;li&gt;Terraform&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Because of Tree Sitter, adding support for other languages is substantially easier now. &lt;strong&gt;Note:&lt;/strong&gt; &lt;code&gt;kondex&lt;/code&gt; also supports parsing our own metadata definition files, which is done through a custom linear scanner instead of tree-sitter.&lt;/p&gt;&#xA;&lt;p&gt;The actual index is stored in SQLite.&lt;/p&gt;&#xA;&lt;h2 id=&#34;building-the-index&#34;&gt;Building the Index&lt;/h2&gt;&#xA;&lt;p&gt;I mentioned before one of the key problems I wanted to solve with &lt;code&gt;kondex&lt;/code&gt; was the cost of building a full index of a repository. Solving this required doing a few tricks that ended up being a very interesting part of the project. The following figure illustrates the process of doing an initial scan for a code base:&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://static.winterdom.com/images/2026/kondex-initial-scan.svg&#34; alt=&#34;kondex initial scan&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;The most obvious initial solution for speed was parallelizing the work: &lt;code&gt;kondex&lt;/code&gt; will indeed scan input files in parallel through the use of multiple threads. However, it has a single thread writing to the SQLite database and it can be the bottlleneck. This is the reason we chose to rebuild indexes at the end of the scan, instead of ensuring they are kept up-to-date as the data is written.&lt;/p&gt;&#xA;&lt;p&gt;Supporting Git Worktrees required getting a bit more creative. Instead of deciding the list of files to scan using the file system, &lt;code&gt;kondex&lt;/code&gt; takes a bit of a different approach:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&lt;code&gt;git ls-files -s&lt;/code&gt; gives us the list of all tracked files in the current worktree alongside its blob SHA.&lt;/li&gt;&#xA;&lt;li&gt;Indexed declarations are stored per blob SHA, and shared by every worktree or branch.&lt;/li&gt;&#xA;&lt;li&gt;Each worktree has it&amp;rsquo;s own snapshot, which tracks the path -&amp;gt; blob list&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;This means that when switching branches or worktrees, &lt;code&gt;kondex&lt;/code&gt; is able to:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Keep a single index across branches or worktrees&lt;/li&gt;&#xA;&lt;li&gt;Quickly figure out the files that have been changed, removed, or added from the current index and do an incremental scan, which can be done by comparing the new blob SHAs against the list already stored in the index.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Let&amp;rsquo;s illustrate this with an example. First, let&amp;rsquo;s build a full index of our current major branch:&lt;/p&gt;&#xA;&lt;div class=&#34;codeblock&#34;&gt;&#xA;  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt; kondex scan&#xA;repo   C:/dev/src/&amp;lt;path&amp;gt;  (worktree: C:/dev/src/v26)&#xA;index  &amp;lt;path&amp;gt;\index.db&#xA;enumerated 145139 tracked files, 55834 indexable, 33173 under excluded directories, 46664 unique blobs (465ms)&#xA;cache: 0 hits, 46664 to read (116ms)&#xA;suspended query indexes and checkpoints for the load; both are done once at the end&#xA;  6162/46664 blobs, 77.0 MB, 129607 declarations&#xA;  12533/46664 blobs, 163.3 MB, 265517 declarations&#xA;  19370/46664 blobs, 260.2 MB, 410976 declarations&#xA;  26076/46664 blobs, 347.3 MB, 546584 declarations&#xA;  29517/46664 blobs, 399.7 MB, 615042 declarations&#xA;  37074/46664 blobs, 490.4 MB, 768798 declarations&#xA;  44792/46664 blobs, 590.0 MB, 923175 declarations&#xA;rebuilt query indexes (1.294s)&#xA;&#xA;scanned v26 at c0049c880&#xA;  tracked    145,139 files (55,834 indexed)&#xA;  blobs      46,664 unique; 0 already cached, 46,597 read&#xA;  cache hit  0.0%&#xA;  read       618.1 MB&#xA;  extracted  963,379 declarations from 46,597 files&#xA;  syntax     326 files had error regions (0.70%); entities may be incomplete&#xA;  skipped    67 over size limit&#xA;  excluded   33,173 files under infoscripts/&#xA;  elapsed    18.9s  (enumerate 465ms, diff 116ms, read 15.29s, index 1.29s, write 1.18s, checkpoint 559ms)&#xA;  writer     busy 11.85s of the 15.29s read (78%), 2.58s of it committing&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;/div&gt;&#xA;&lt;p&gt;You can see doing a full index of over 45K files was done in less than 20 seconds. This is good enough that we can use &lt;code&gt;kondex&lt;/code&gt; even without forcing the developer to manually trigger a full scan the first time the tool is used!&lt;/p&gt;&#xA;&lt;p&gt;Now let&amp;rsquo;s switch to a worktree tied to a different major branch, and do an incremental scan:&lt;/p&gt;&#xA;&lt;div class=&#34;codeblock&#34;&gt;&#xA;  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt; kondex scan&#xA;repo   C:/dev/src/&amp;lt;path&amp;gt;  (worktree: C:/dev/src/v25)&#xA;index  &amp;lt;path&amp;gt;\index.db&#xA;enumerated 147991 tracked files, 54909 indexable, 35470 under excluded directories, 41853 unique blobs (497ms)&#xA;cache: 34322 hits, 7531 to read (91ms)&#xA;suspended query indexes and checkpoints for the load; both are done once at the end&#xA;  2886/7531 blobs, 77.5 MB, 70259 declarations&#xA;  5915/7531 blobs, 177.1 MB, 150643 declarations&#xA;rebuilt query indexes (1.71s)&#xA;&#xA;scanned v25 at 521d30840&#xA;  tracked    147,991 files (54,909 indexed)&#xA;  blobs      41,853 unique; 34,322 already cached, 7,493 read&#xA;  cache hit  82.0%&#xA;  read       225.5 MB&#xA;  extracted  192,292 declarations from 7,493 files&#xA;  syntax     55 files had error regions (0.73%); entities may be incomplete&#xA;  skipped    38 over size limit&#xA;  excluded   35,470 files under infoscripts/&#xA;  elapsed    9.85s  (enumerate 497ms, diff 91ms, read 5.63s, index 1.71s, write 1.12s, checkpoint 810ms)&#xA;  writer     busy 4.14s of the 5.63s read (73%), 714ms of it committing&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;/div&gt;&#xA;&lt;p&gt;Now we see the incremental scan for a different (older) branch being done in less than 10 seconds. This is a fairly high value due to the number of modified files, but branches with fewer changes will see very fast scans.&lt;/p&gt;&#xA;&lt;h2 id=&#34;whats-next&#34;&gt;What&amp;rsquo;s next?&lt;/h2&gt;&#xA;&lt;p&gt;I will continue going over &lt;code&gt;kondex&lt;/code&gt; functionality in a later post, by showing what you can do once an index has been built.&lt;/p&gt;&#xA;</description>
				<pubDate>Mon, 14 Sep 2026 00:00:00 +0000</pubDate>
				<link>https://winterdom.com/2026/09/14/kondex-a-declaration-indexing-tool/</link>
				<guid isPermaLink="true">https://winterdom.com/2026/09/14/kondex-a-declaration-indexing-tool/</guid>
			</item>
		
			<item>
				<title>Azure File Storage and Billing Transparency</title>
				<description>&lt;p&gt;&lt;a href=&#34;https://docs.microsoft.com/en-us/azure/storage/files/?toc=%2fazure%2fstorage%2ffiles%2ftoc.json&#34;&gt;Azure Files&lt;/a&gt;&#xA;is an excellent service, providing file-system (SMB) based storage on the cloud. It has been&#xA;very useful for us together with Azure Kubernetes Service.&lt;/p&gt;&#xA;&lt;p&gt;Functionality-wise, I have no complaints. While there are a few things that could be better, the&#xA;service overall provides good value, and the addition of Premium File Storage is definitely&#xA;a very interesting and exciting option.&lt;/p&gt;&#xA;&lt;p&gt;I have been, however, dissatisfied with the Billing experience.&lt;/p&gt;&#xA;&lt;h2 id=&#34;cloud-billing-transparency&#34;&gt;Cloud Billing Transparency&lt;/h2&gt;&#xA;&lt;p&gt;I&amp;rsquo;m a strong believer that Cloud-based offerings need to have a clear set of billing rules&#xA;that customers can clearly understand and use to budget their cloud costs. There are 4 key&#xA;elements that I believe contribute to having clear and transparent cloud billing for&#xA;usage-based services:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;The publicly available pricing information should clearly document what&#xA;&lt;strong&gt;billing meters&lt;/strong&gt; are used and how these relate to the prices listed.&lt;/li&gt;&#xA;&lt;li&gt;For services that charge for communication, any related billing meters should have&#xA;a clear relation to the &lt;strong&gt;underlying protocol&lt;/strong&gt; operations and semantics.&lt;/li&gt;&#xA;&lt;li&gt;The service should provide &lt;strong&gt;metrics&lt;/strong&gt; that allow customers to understand their usage.&lt;/li&gt;&#xA;&lt;li&gt;Any metrics should clearly &lt;strong&gt;relate&lt;/strong&gt; to the corresponding billing meters, so that&#xA;the customer can audit their bills for accuracy.&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;A customer that understands how your service is billed, budget properly for it,&#xA;and also audit the usage is a happy customer.&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-azure-files-billing-conundrum&#34;&gt;The Azure Files Billing Conundrum&lt;/h2&gt;&#xA;&lt;p&gt;Why am I generally unhappy about how Azure Files are billed? It&amp;rsquo;s not so much that I object&#xA;to the actual cost of the service (which is generally reasonable for most scenarios), but that&#xA;I believe that Azure Files breaks several of the rules above.&lt;/p&gt;&#xA;&lt;p&gt;Let me explain why.&lt;/p&gt;&#xA;&lt;h3 id=&#34;pricing-information-vs-billing-meters&#34;&gt;Pricing Information vs. Billing Meters&lt;/h3&gt;&#xA;&lt;p&gt;The first issue I have with Azure Files is how &lt;a href=&#34;https://azure.microsoft.com/en-us/pricing/details/storage/files/&#34;&gt;pricing information&lt;/a&gt;&#xA;is presented. There are two separate components: Storage and Operations.&lt;/p&gt;&#xA;&lt;p&gt;Storage pricing is very clearly presented, but Operation pricing is &lt;strong&gt;very deceptive&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Billing meters are only partially related to how pricing is presented. While pricing talks about&#xA;&amp;ldquo;Put, Create Container Operations&amp;rdquo; or &amp;ldquo;List Operations&amp;rdquo;, billing meters include things such&#xA;as &amp;ldquo;protocol operations&amp;rdquo;, and &amp;ldquo;lrs write operations&amp;rdquo;. You can guess about how they are mapped&#xA;in some cases, but not all of them.&lt;/p&gt;&#xA;&lt;p&gt;I would much rather the pricing information was presented by outlining the meters used and&#xA;what each one measures exactly.&lt;/p&gt;&#xA;&lt;h3 id=&#34;billing-and-protocols&#34;&gt;Billing and Protocols&lt;/h3&gt;&#xA;&lt;p&gt;Azure Files operation pricing is stated in terms of the underlying operations on the REST API. This&#xA;is fine if you&amp;rsquo;re accessing the service over the HTTP interface, but that&amp;rsquo;s not really the common usage&#xA;for Azure Files.&lt;/p&gt;&#xA;&lt;p&gt;Instead, you&amp;rsquo;ll mostly be accessing the service over SMB from Windows or Linux machines, and so there&amp;rsquo;s&#xA;no clear relationship of how a File-System-based operation (such as &lt;code&gt;Open File&lt;/code&gt;) maps to an operation&#xA;on the REST API. This leads to significant confusion and makes it easy to budget incorrectly for the service,&#xA;understimating the actual costs.&lt;/p&gt;&#xA;&lt;p&gt;For example, I ran into a case where a service would often open a file, read from it, and then close it.&#xA;I was pretty surprised that my Azure Files bill was primarily composed of &lt;strong&gt;&amp;ldquo;Write Operations&amp;rdquo;&lt;/strong&gt;. Well,&#xA;surprise-surprise, turns out that the file-system  &lt;em&gt;open a file&lt;/em&gt; operation is considered a write operation for&#xA;billing purposes. There was absolutely no way for me to figure that out before I wasted a lot of time&#xA;and a few support cases (which went nowhere) on it.&lt;/p&gt;&#xA;&lt;h3 id=&#34;meters-and-metrics&#34;&gt;Meters and Metrics&lt;/h3&gt;&#xA;&lt;p&gt;Azure Files supports some basic metrics, which get stored in Azure Storage Tables on the same&#xA;storage account. While these are useful in general, they are not very useful to audit your usage costs.&lt;/p&gt;&#xA;&lt;p&gt;This is because the metrics, again, bear no direct relationship to the billing meters. For example, you will&#xA;see things like this in your storage metrics:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;user;Create&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;user;SessionSetup&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;user;Write&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;user;GetFile&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;user;QueryInfo&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;&amp;hellip; and more&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;These bear a little bit more relationship to the file-system semantics of Azure Files access over SMB&#xA;(but still only partially). The big problem is that there&amp;rsquo;s no obvious way to relate the metric values&#xA;to the corresponding Billing Meters being used. We can guess about what some of it means, but&#xA;since it&amp;rsquo;s essentially undocumented, it does not provide a base that&amp;rsquo;s stable enough to actually&#xA;audit your bill.&lt;/p&gt;&#xA;&lt;p&gt;It also clearly doesn&amp;rsquo;t relate very clearly to the public pricing information, either.&lt;/p&gt;&#xA;&lt;p&gt;But beyond the obvious disconnect with pricing/meters, there&amp;rsquo;s an even more annoying issue with&#xA;Azure Files metrics: They don&amp;rsquo;t provide enough information to actually troubleshoot billing issues,&#xA;because there are no corresponding access logs for Azure Files.&lt;/p&gt;&#xA;&lt;p&gt;So if my Azure Files bill grows 10x in write operations, there doesn&amp;rsquo;t appear to be a way to figure out&#xA;what shares, folders, or files are actually getting accessed and thus triggering the usage increase. Even&#xA;Microsoft Support appears unable to obtain this information, which means that if you run into such a case,&#xA;you&amp;rsquo;re stuck trying to figure things out by trial and error.&lt;/p&gt;&#xA;&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;&#xA;&lt;p&gt;While Azure Files provides an excellent platform for cloud services, make sure you understand exactly&#xA;what your usage patterns are and keep in mind that the file-system nature of the service seems to be&#xA;very much at odds with how the pricing information is presented. Expecting some surprises in your bill&#xA;while you understand your usage patterns better might be a good idea, too.&lt;/p&gt;&#xA;</description>
				<pubDate>Wed, 11 Dec 2019 00:00:00 +0000</pubDate>
				<link>https://winterdom.com/2019/12/11/azure-files-and-billing-transparency/</link>
				<guid isPermaLink="true">https://winterdom.com/2019/12/11/azure-files-and-billing-transparency/</guid>
			</item>
		
			<item>
				<title>Importing Certificates into Azure Key Vault using the API</title>
				<description>&lt;p&gt;I spent some time the last two days figuring out how to correctly import X.509&#xA;certificates into an Azure &lt;a href=&#34;https://docs.microsoft.com/en-us/azure/key-vault/&#34;&gt;Key Vault&lt;/a&gt; instance using&#xA;the API (through the &lt;a href=&#34;https://www.nuget.org/packages/Microsoft.Azure.KeyVault&#34;&gt;Microsoft.Azure.KeyVault&lt;/a&gt;&#xA;NuGet package), and ran into a few issues.&lt;/p&gt;&#xA;&lt;p&gt;Unfortunately, neither the library, nor the underlying REST API are very well documented, so wanted to&#xA;write this post to document the gotchas I ran into, in case it&amp;rsquo;s useful to anyone.&lt;/p&gt;&#xA;&lt;h2 id=&#34;pem-certificate-format&#34;&gt;PEM Certificate Format&lt;/h2&gt;&#xA;&lt;p&gt;The &lt;a href=&#34;https://docs.microsoft.com/en-us/rest/api/keyvault/importcertificate/importcertificate&#34;&gt;Import Certificate&lt;/a&gt;&#xA;API (and corresponding &lt;code&gt;KeyVault.ImportCertificate()&lt;/code&gt; method) are documented as accepting the certificate&#xA;to import in both &lt;code&gt;PFX&lt;/code&gt; and &lt;code&gt;PEM&lt;/code&gt; formats. It took me a while to figure out the right incantation&#xA;to use for a &lt;code&gt;PEM&lt;/code&gt; file, however.&lt;/p&gt;&#xA;&lt;p&gt;There are a few separate issues here you need to be very careful about:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;Private Key Format&lt;/strong&gt;: A certificate with a private key in a PEM file could have the key stored&#xA;in various format. One very common one is &lt;code&gt;PKCS#1&lt;/code&gt;, which is identified by the key being wrapped in&#xA;&lt;code&gt;-----BEGIN RSA PRIVATE KEY-----&lt;/code&gt; and &lt;code&gt;-----END RSA PRIVATE KEY-----&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;p&gt;If you try to import such a certificate to Key Vault, however, you will get an error. This is because&#xA;Key Vault will only accept a key in &lt;code&gt;PKCS#8&lt;/code&gt; format, which you will recognize because it&amp;rsquo;s wrapped&#xA;in &lt;code&gt;-----BEGIN PRIVATE KEY-----&lt;/code&gt; and &lt;code&gt;-----END PRIVATE KEY-----&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;p&gt;You can easily use the &lt;code&gt;openssl pkcs8 -topk8&lt;/code&gt; command to convert the private key once you know to do this.&lt;/p&gt;&#xA;&lt;p&gt;This aspect is not documented on the API, but it is mentioned in passing in the&#xA;(Key Vault documentation)[https://docs.microsoft.com/en-us/azure/key-vault/certificate-scenarios#formats-of-import-we-support].&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;Content Type&lt;/strong&gt;: If you&amp;rsquo;re going to import a &lt;code&gt;PEM&lt;/code&gt; certificate, you also need to set the &lt;code&gt;policy.secret_props.contentType&lt;/code&gt;&#xA;property to the right type:&lt;/p&gt;&#xA;&lt;div class=&#34;codeblock&#34;&gt;&#xA;  &lt;div class=&#34;codeblock__bar&#34;&gt;&#xA;    &lt;span class=&#34;codeblock__name&#34;&gt;cs&lt;/span&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-cs&#34; data-lang=&#34;cs&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;policy&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;CertificatePolicy&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;SecretProperties&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;SecretProperties&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;ContentType&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;application/x-pem-file&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&lt;/div&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;Value Format&lt;/strong&gt;: The second confusing part I ran into here is how to actually send the &lt;code&gt;PEM&lt;/code&gt; data to&#xA;KeyVault. The API documentation states that the &lt;code&gt;value&lt;/code&gt; parameter is &lt;code&gt;Base64 encoded representation of the certificate object to import.&lt;/code&gt; This is only partially correct.&lt;/p&gt;&#xA;&lt;p&gt;If you&amp;rsquo;re importing a certificate in &lt;code&gt;PFX&lt;/code&gt; format, this is correct. &lt;code&gt;PFX&lt;/code&gt; is a binary format, so you need to&#xA;base64-encode the value before providing it to Key Vault. &lt;code&gt;PEM&lt;/code&gt;, however, is already a text format, so&#xA;base64-encoding it will make no sense.&lt;/p&gt;&#xA;&lt;p&gt;The result is that the &lt;code&gt;value&lt;/code&gt; parameter will just be the raw text contents of your &lt;code&gt;PEM&lt;/code&gt; file. With one&#xA;major, undocumented gotcha: The content &lt;strong&gt;must&lt;/strong&gt; use UNIX-style line separators (&lt;code&gt;\n&lt;/code&gt;). Attempting to&#xA;send the content using Windows-style line endings (&lt;code&gt;\r\n&lt;/code&gt;) will just result in a confusing error&#xA;such as &lt;code&gt;The specified PEM X.509 certificate content is in an unexpected format. Please check if certificate is in valid PEM format&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;p&gt;The only way I was able to figure out this little detail was by reading the &lt;a href=&#34;https://github.com/azure/azure-cli&#34;&gt;Azure CLI&lt;/a&gt;&#xA;source code.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;updating-certificates&#34;&gt;Updating Certificates&lt;/h2&gt;&#xA;&lt;p&gt;Another interesting scenario I ran into was attempting to import a new certificate version on top of an&#xA;existing certificate as a new version. The documentation for the&#xA;&lt;a href=&#34;https://docs.microsoft.com/en-us/rest/api/keyvault/importcertificate/importcertificate&#34;&gt;Import Certificate&lt;/a&gt;&#xA;API doesn&amp;rsquo;t actually say if this is possible, but since the Azure Portal actually allows you to do it,&#xA;I figured it would be a safe bet that it would work.&lt;/p&gt;&#xA;&lt;p&gt;It is, indeed, possible to do this. However, there was one surprising error I ran into while testing this out.&lt;/p&gt;&#xA;&lt;p&gt;Purely by coincidence, I tried importing a certificate that had a 4096-bit key as a new version of a previous&#xA;certificate that had a 2048-bit key. Trying this out failed with a &lt;code&gt;CONFLICT&lt;/code&gt; error. Digging deeper, I found&#xA;out that the actual error message was &lt;code&gt;Expected KeySize is 4096 but was 2048&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;p&gt;This made no sense at first, until I realized that Key Vault was apparently attempting to somehow reuse the key of the&#xA;current certificate version rather than the one included in the &lt;code&gt;Import Certificate&lt;/code&gt; call.&lt;/p&gt;&#xA;&lt;p&gt;The way to avoid this error appears to be to explicitly set the &lt;code&gt;policy.key_props.key_size&lt;/code&gt; parameter to the right&#xA;value when attempting to import the new certificate version on top of the old one:&lt;/p&gt;&#xA;&lt;div class=&#34;codeblock&#34;&gt;&#xA;  &lt;div class=&#34;codeblock__bar&#34;&gt;&#xA;    &lt;span class=&#34;codeblock__name&#34;&gt;cs&lt;/span&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-cs&#34; data-lang=&#34;cs&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;policy&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;CertificatePolicy&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;Attributes&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;CertificateAttributes&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;Enabled&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;SecretProperties&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;SecretProperties&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;ContentType&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;application/x-pem-file&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;KeyProperties&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;KeyProperties&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;Exportable&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;ReuseKey&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;KeySize&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;theCertificate&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;PublicKey&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Key&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;KeySize&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&lt;/div&gt;&#xA;&lt;p&gt;Hope you find this useful!&lt;/p&gt;&#xA;</description>
				<pubDate>Thu, 31 Oct 2019 00:00:00 +0000</pubDate>
				<link>https://winterdom.com/2019/10/31/importing-keyvault-certificates-api/</link>
				<guid isPermaLink="true">https://winterdom.com/2019/10/31/importing-keyvault-certificates-api/</guid>
			</item>
		
			<item>
				<title>Defining RBAC Role Assignments in ARM Templates</title>
				<description>&lt;p&gt;It&amp;rsquo;s no secret I&amp;rsquo;m a big fan of Azure Resource Manager (ARM) templates. Getting&#xA;started with ARM templates is hard, but well worth the effort, and make it significantly&#xA;easier to have reproduceable, consistent deployments of your Azure resources.&lt;/p&gt;&#xA;&lt;p&gt;One thing that I had been feeling left out, however, was being able to&#xA;assign permissions to Azure resources during creation. Azure&amp;rsquo;s Role-based Access Control&#xA;(RBAC) mechanism is a powerful way to control who can manage and access your resources,&#xA;and having to do this through scripting was possible, but cumbersome at times.&lt;/p&gt;&#xA;&lt;p&gt;A few days ago, I realized that you can actually&#xA;&lt;a href=&#34;https://github.com/Azure/azure-quickstart-templates/tree/master/101-rbac-builtinrole-resourcegroup&#34;&gt;create RBAC role assignments&lt;/a&gt;&#xA;through ARM templates just like any other resource. This capability is not new by any means, I&#xA;just had missed it before!&lt;/p&gt;&#xA;&lt;h2 id=&#34;creating-an-assignment&#34;&gt;Creating an assignment&lt;/h2&gt;&#xA;&lt;p&gt;To create an assignment, you need the following information:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;The ID of the role you want to assign. This is a long string that contains the&#xA;subscription id and the role identifier (both GUIDs).&lt;/li&gt;&#xA;&lt;li&gt;The object ID of the user/group/service principal you want to grant access to.&lt;/li&gt;&#xA;&lt;li&gt;The scope at which you want to assign the role, which is going to be either a&#xA;subscription, resource group, or resource id.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Here&amp;rsquo;s an example of creating such an assignment:&lt;/p&gt;&#xA;&lt;div class=&#34;codeblock&#34;&gt;&#xA;  &lt;div class=&#34;codeblock__bar&#34;&gt;&#xA;    &lt;span class=&#34;codeblock__name&#34;&gt;json&lt;/span&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;#34;$schema&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;#34;contentVersion&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;1.0.0.0&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;#34;parameters&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;monitoringUsersGroup&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;string&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;#34;metadata&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;description&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Object ID for the monitoring users group in AAD&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;#34;variables&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;monitoringContributorRole&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;[concat(subscription().Id, &amp;#39;/providers/Microsoft.Authorization/roleDefinitions/749f88d5-cbae-40b8-bcfc-e573ddc772fa&amp;#39;)]&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;#34;resources&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Microsoft.Authorization/roleAssignments&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;ace492e6-ebf6-48fa-8dd4-ea762489cbf3&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;#34;apiVersion&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;2017-10-01-preview&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;#34;properties&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;roleDefinitionId&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;[variables(&amp;#39;monitoringContributorRole&amp;#39;)]&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;principalId&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;[parameters(&amp;#39;monitoringUsersGroup&amp;#39;)]&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;scope&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;[resourceGroup().Id]&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;#34;outputs&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&lt;/div&gt;&#xA;&lt;p&gt;Here we grant the members of an Azure Active Directory group the &lt;code&gt;Monitoring Contributor&lt;/code&gt;&#xA;built-in role to the resource group the template is deployed to.&lt;/p&gt;&#xA;&lt;p&gt;Also interesting here is that you don&amp;rsquo;t need to specify a &lt;code&gt;location&lt;/code&gt; property in the resource.&lt;/p&gt;&#xA;&lt;h2 id=&#34;some-gotchas&#34;&gt;Some gotchas&lt;/h2&gt;&#xA;&lt;p&gt;There are a couple of things to watch out for when doing this.&lt;/p&gt;&#xA;&lt;p&gt;The first one is that to assign a role, you need the &lt;code&gt;objectId&lt;/code&gt; of the AAD user/group/principal,&#xA;rather than the name. This is cumbersome because there&amp;rsquo;s no way to resolve these within&#xA;the ARM template itself, so you&amp;rsquo;ll always need to pass these as input parameters.&lt;/p&gt;&#xA;&lt;p&gt;A more significant issue, however, is the &lt;code&gt;name&lt;/code&gt; of the &lt;code&gt;roleAssignment&lt;/code&gt; resource, which&#xA;needs to be a unique GUID.&lt;/p&gt;&#xA;&lt;p&gt;This is a problem if, for example, you&amp;rsquo;re assigning role permissions at the resource group&#xA;or individual resource level, rather than globally at the subscription.&lt;/p&gt;&#xA;&lt;p&gt;For example, in my case I was creating a template that would be used to deploy multiple&#xA;copies of the same resources into different resource groups within the same subscription.&lt;/p&gt;&#xA;&lt;p&gt;If the GUID that defines the role assignment name is hardcoded in the template, then&#xA;each time I ran the template, the &lt;code&gt;scope&lt;/code&gt; of the role assignment would get overwritten with&#xA;the id of the last resource group it was deployed to. Clearly, this is undesirable.&lt;/p&gt;&#xA;&lt;p&gt;What we need then, is a way to ensure that each deployment to a different resource group&#xA;uses a different GUID for the role assignment, but at the same time, ensure that the same&#xA;one is used when deploying to the same resource group.&lt;/p&gt;&#xA;&lt;p&gt;Clearly, providing the assignment GUID as a parameter is an easy workaround, but very cumbersome.&lt;/p&gt;&#xA;&lt;p&gt;A better workaround comes from the &lt;a href=&#34;https://docs.microsoft.com/en-us/azure/azure-resource-manager/resource-group-template-functions-string#guid&#34;&gt;guid&lt;/a&gt;&#xA;function! It takes one or more strings that are used to calculate a hash, very much&#xA;like the &lt;code&gt;uniquestring&lt;/code&gt; function; only this one generates a string in GUID format instead.&lt;/p&gt;&#xA;&lt;p&gt;By using the &lt;code&gt;guid&lt;/code&gt; function with the resource group id and some other consistent stuff as input,&#xA;we can solve our problem in an elegant way:&lt;/p&gt;&#xA;&lt;div class=&#34;codeblock&#34;&gt;&#xA;  &lt;div class=&#34;codeblock__bar&#34;&gt;&#xA;    &lt;span class=&#34;codeblock__name&#34;&gt;json&lt;/span&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Microsoft.Authorization/roleAssignments&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;[guid(resourceGroup().id, &amp;#39;monitoringUsers&amp;#39;)]&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&lt;/div&gt;&#xA;</description>
				<pubDate>Thu, 02 Aug 2018 00:00:00 +0000</pubDate>
				<link>https://winterdom.com/2018/08/02/rbac-role-assignments-in-arm-templates/</link>
				<guid isPermaLink="true">https://winterdom.com/2018/08/02/rbac-role-assignments-in-arm-templates/</guid>
			</item>
		
			<item>
				<title>AzureFile Persistent Volumes Retain Issue</title>
				<description>&lt;p&gt;A bit ago, I &lt;a href=&#34;https://winterdom.com/2018/07/23/aks-azure-files-permissions/&#34;&gt;posted&lt;/a&gt; about some issues around permissions&#xA;when using static provisioning of Azure File volumes in Azure Kubernetes Service (AKS). In there, I mentioned that the&#xA;workaround was to use explicit Persistent Volumes so that the right mount options could be created.&lt;/p&gt;&#xA;&lt;p&gt;Since then, I&amp;rsquo;ve run into a separate, but related, issue. I started noticing that sometimes, my Persistent Volume Claims&#xA;would get stuck on the &lt;code&gt;PENDING&lt;/code&gt; state:&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;%7B%7Bsite.images_base%7D%7D/2018/aks-azurefiles-2.png&#34; alt=&#34;Pending Persistent Volume Claims&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;The notes for the PVCs would not contain any useful information. But looking at the Persistent Volumes, I noticed&#xA;they would get stuck in the &lt;code&gt;RELEASED&lt;/code&gt; state:&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;%7B%7Bsite.images_base%7D%7D/2018/aks-azurefiles-3.png&#34; alt=&#34;Released Persistent Volumes&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;I could not figured out exactly what was happening until I noticed the pattern that led to it:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;I&amp;rsquo;d create the Persistent Volumes&lt;/li&gt;&#xA;&lt;li&gt;Then I&amp;rsquo;d deploy an application on the cluster that would create Persistent Volume Claims on the volumes&lt;/li&gt;&#xA;&lt;li&gt;I&amp;rsquo;d update the application. This worked just fine and stuff continued to run.&lt;/li&gt;&#xA;&lt;li&gt;I&amp;rsquo;d remove the application (thus deleting the Persistent Volume Claims)&lt;/li&gt;&#xA;&lt;li&gt;I&amp;rsquo;d deploy the application again, and now the PVCs would get stuck in &lt;code&gt;Pending&lt;/code&gt; state.&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;The issue of course, happened in step (4) above. Once the Persistent Volume Claim was deleted, the Persistent Volume&#xA;would transitioned to the &lt;code&gt;Released&lt;/code&gt; status. These volumes all had a default&#xA;&lt;a href=&#34;https://kubernetes.io/docs/tasks/administer-cluster/change-pv-reclaim-policy/&#34;&gt;reclaim policy&lt;/a&gt; of &lt;code&gt;Retain&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Based on my understanding, I would have expected that taking a new claim on the volumes would work. But unfortunately,&#xA;this does not appear to be the case (either I&amp;rsquo;m misunderstanding the Kubernetes documentation, or there&amp;rsquo;s an issue&#xA;here in the volume provider).&lt;/p&gt;&#xA;&lt;p&gt;I also thought that maybe making this work would involve setting the retain policy to &lt;code&gt;Recycle&lt;/code&gt;, but this appears&#xA;to be unimplemented as of yet for the azureFile provider.&lt;/p&gt;&#xA;&lt;p&gt;In any case, once this happens, the only way to get this working again is to manually delete the Persistent Volumes&#xA;and recreate them again from scratch.&lt;/p&gt;&#xA;&lt;p&gt;This is unexpected (not explicitly documented), not obvious, and certainly annoying.:w&lt;/p&gt;&#xA;</description>
				<pubDate>Thu, 26 Jul 2018 00:00:00 +0000</pubDate>
				<link>https://winterdom.com/2018/07/26/azurefile-persistent-volumes-retain-issue/</link>
				<guid isPermaLink="true">https://winterdom.com/2018/07/26/azurefile-persistent-volumes-retain-issue/</guid>
			</item>
		
			<item>
				<title>Issues when deleting azureFile dynamic volumes in Kubernetes</title>
				<description>&lt;p&gt;I&amp;rsquo;ve been doing a lot of work lately with Kubernetes and Azure Kubernetes Service in particular. For this,&#xA;I&amp;rsquo;m using the azureFile storage provider to support providing storage folders for my pods.&lt;/p&gt;&#xA;&lt;p&gt;For one specific case, I&amp;rsquo;m using dynamic provisioning of persistent volumes, as described in the&#xA;&lt;a href=&#34;https://docs.microsoft.com/en-us/azure/aks/azure-files-dynamic-pv&#34;&gt;documentation&lt;/a&gt;. This has been working great.&#xA;Almost.&lt;/p&gt;&#xA;&lt;p&gt;Today I noticed something odd. Some of the Persistent Volumes created were stuck on a failed state&#xA;during deletion:&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;%7B%7Bsite.images_base%7D%7D/2018/aks-azurefiles-1.png&#34; alt=&#34;Failed Persistent Volumes&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;The error message was this:&lt;/p&gt;&#xA;&lt;div class=&#34;codeblock&#34;&gt;&#xA;  &lt;div class=&#34;codeblock__bar&#34;&gt;&#xA;    &lt;span class=&#34;codeblock__name&#34;&gt;text&lt;/span&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Failed to create deleter for volume &amp;#34;pvc-85ba62d5-902e-11e8-9771-2a03d0b14ac3&amp;#34;: Couldn&amp;#39;t get secret 6d07576ce8d89bd641b5/azure-storage-account-fd9063993902011e888c32e-secret&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&lt;/div&gt;&#xA;&lt;p&gt;This actually made sense. The way dynamic persistent volumes work is this:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;When the Persistent Volume Claim is created, Kubernetes creates a new Secret object containing the&#xA;credentials for the Azure Storage Account. This secret is named &lt;code&gt;azure-storage-account-&amp;lt;namespace&amp;gt;-secret&lt;/code&gt;.&lt;/li&gt;&#xA;&lt;li&gt;When the Persistent Volume Claim is deleted, the deleter for the volume will use these credentials&#xA;to delete the Azure File Share that was dynamically created, and then delete the corresponding secret.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;The second part is what was biting me.&lt;/p&gt;&#xA;&lt;p&gt;I currently deploy the applications using &lt;a href=&#34;https://docs.helm.sh/&#34;&gt;Helm&lt;/a&gt;. I deploy each one into a separate Kubernetes&#xA;Namespace. This is fully supported by Helm, but has one issue: When you delete the application using&#xA;&lt;code&gt;helm delete&lt;/code&gt;, the original namespace created remains behind. I wanted to clean these up, and so&#xA;was aggressively deleting them using &lt;code&gt;kubectl delete namespace&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;p&gt;The problem here is that the secret that gets automatically created by the azureFiles provider when&#xA;the Persistent Volume Claim is created, goes into the same namespace as the PVC. That meant that when I was&#xA;aggressively removing the namespace, I was also deleting the secret before the volume deleter could get to it.&lt;/p&gt;&#xA;&lt;p&gt;This isn&amp;rsquo;t very clearly documented (or at least I found no mention of this), so it might be something to watch out for.&lt;/p&gt;&#xA;</description>
				<pubDate>Thu, 26 Jul 2018 00:00:00 +0000</pubDate>
				<link>https://winterdom.com/2018/07/26/kubernetes-azureFile-dynamic-volumes-deleting/</link>
				<guid isPermaLink="true">https://winterdom.com/2018/07/26/kubernetes-azureFile-dynamic-volumes-deleting/</guid>
			</item>
		
			<item>
				<title>AKS Service Principal Credentials</title>
				<description>&lt;p&gt;When creating a new Azure Kubernetes Service (AKS) cluster, you must define&#xA;a Service Principal in your Azure Active Directory Tenant that will&#xA;be used by the cluster to do operations on the Azure infrastructure later on.&lt;/p&gt;&#xA;&lt;p&gt;The &lt;a href=&#34;https://docs.microsoft.com/en-us/azure/aks/kubernetes-service-principal&#34;&gt;documentation&lt;/a&gt; states:&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;On the master and node VMs in the Kubernetes cluster, the service principal&#xA;credentials are stored in the file &lt;code&gt;/etc/kubernetes/azure.json&lt;/code&gt;&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;I was curious how this worked, so I set out to take a look at the file. An easy way to do this&#xA;was to spawn a new &lt;code&gt;busybox&lt;/code&gt; pod on the cluster with the &lt;code&gt;azure.json&lt;/code&gt; file&#xA;mounted as a volume into the container.&lt;/p&gt;&#xA;&lt;p&gt;I used the following YAML template to create the pod using &lt;code&gt;kubectl apply&lt;/code&gt;:&lt;/p&gt;&#xA;&lt;div class=&#34;codeblock&#34;&gt;&#xA;  &lt;div class=&#34;codeblock__bar&#34;&gt;&#xA;    &lt;span class=&#34;codeblock__name&#34;&gt;yaml&lt;/span&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;apiVersion&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;v1&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;kind&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;Pod&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;metadata&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;test&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;spec&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;containers&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;test&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;image&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;busybox&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;command&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;sleep&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;s2&#34;&gt;&amp;#34;3600&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;volumeMounts&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;nt&#34;&gt;mountPath&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;/etc/kubernetes/azure.json&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;azure-secret&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;volumes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;azure-secret&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;hostPath&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;path&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;/etc/kubernetes/azure.json&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;File&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&lt;/div&gt;&#xA;&lt;p&gt;Once the pod is up and running, I went spelunking into it by opening a new interactive session&#xA;using &lt;code&gt;kubectl exec -it test -- sh&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;p&gt;First, let&amp;rsquo;s look at the contents of the file through &lt;code&gt;cat /etc/kubernetes/azure.json&lt;/code&gt;:&lt;/p&gt;&#xA;&lt;div class=&#34;codeblock&#34;&gt;&#xA;  &lt;div class=&#34;codeblock__bar&#34;&gt;&#xA;    &lt;span class=&#34;codeblock__name&#34;&gt;json&lt;/span&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;cloud&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;AzurePublicCloud&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;tenantId&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;*********&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;subscriptionId&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;*********&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;aadClientId&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;**********&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;aadClientSecret&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;*********&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;resourceGroup&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;MC_dev-k-eastus-2-rg_dev-k-eastus-2_eastus&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;location&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;eastus&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;vmType&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;standard&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;subnetName&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;aks-subnet&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;securityGroupName&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;aks-agentpool-22088002-nsg&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;vnetName&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;aks-vnet-22088002&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;vnetResourceGroup&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;routeTableName&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;aks-agentpool-22088002-routetable&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;primaryAvailabilitySetName&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;agentpool-availabilitySet-22088002&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;primaryScaleSetName&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;cloudProviderBackoff&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;cloudProviderBackoffRetries&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;cloudProviderBackoffExponent&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;cloudProviderBackoffDuration&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;cloudProviderBackoffJitter&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;cloudProviderRatelimit&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;cloudProviderRateLimitQPS&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;cloudProviderRateLimitBucket&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;useManagedIdentityExtension&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;useInstanceMetadata&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;providerVaultName&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;providerKeyName&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;k8s&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;providerKeyVersion&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&lt;/div&gt;&#xA;&lt;p&gt;The relevant fields containing the credentials are &lt;code&gt;aadClientId&lt;/code&gt; and &lt;code&gt;aadClientSecret&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;p&gt;I was curious, however, as to the permissions on the file:&lt;/p&gt;&#xA;&lt;div class=&#34;codeblock&#34;&gt;&#xA;  &lt;div class=&#34;codeblock__bar&#34;&gt;&#xA;    &lt;span class=&#34;codeblock__name&#34;&gt;text&lt;/span&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;/ # ls -l /etc/kubernetes&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;total 4&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;-rw-------    1 root     root          1173 Jul 24 19:13 azure.json&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&lt;/div&gt;&#xA;&lt;p&gt;As suspected, the file is only readable by containers running as root.&lt;/p&gt;&#xA;</description>
				<pubDate>Tue, 24 Jul 2018 00:00:00 +0000</pubDate>
				<link>https://winterdom.com/2018/07/24/aks-service-principal-credentials/</link>
				<guid isPermaLink="true">https://winterdom.com/2018/07/24/aks-service-principal-credentials/</guid>
			</item>
		
			<item>
				<title>AKS and Azure Files Permissions</title>
				<description>&lt;p&gt;Saving this here for my own recollection later on. Warning, a bit of ranting ahead.&lt;/p&gt;&#xA;&lt;p&gt;Recently, I&amp;rsquo;ve been running a lot of trials on top of the Azure managed Kubernetes&#xA;Service (AKS). One key feature that I needed was the ability to provide services&#xA;deployed to an AKS cluster with external storage. Since I needed the option&#xA;to mount the storage shared between multiple pod instances, Azure Files was the way to go.&lt;/p&gt;&#xA;&lt;p&gt;My first attempt to do this leveraged using automated static volume provisioning,&#xA;as defined in the &lt;a href=&#34;https://docs.microsoft.com/en-us/azure/aks/azure-files-volume&#34;&gt;documentation&lt;/a&gt;.&#xA;&lt;em&gt;Static&lt;/em&gt; just means that you first create the Azure File Share on your own,&#xA;and then provide the pod with the necessary information to reach it, such as the&#xA;account name and key, and the file share name.&lt;/p&gt;&#xA;&lt;p&gt;This worked, sort of. There&amp;rsquo;s a big gotcha that&amp;rsquo;s not included in the documentation, and&#xA;that takes a bit of search-fu to &lt;a href=&#34;http://unethicalblogger.com/2017/12/01/aks-storage-research.html&#34;&gt;find it&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;The problem is &lt;em&gt;file permissions&lt;/em&gt;. When using static provisioning, the Azure Files plugin&#xA;in Kubernetes will default the share permissions to &lt;code&gt;0750&lt;/code&gt; or &lt;code&gt;0700&lt;/code&gt; depending on the version&#xA;of Kubernetes in use.&lt;/p&gt;&#xA;&lt;p&gt;What this means is that static provisioning, as described in the AKS documentation,&#xA;is &lt;em&gt;completely useless&lt;/em&gt; if the following conditions are met:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Your container is configured to run as a non-root user (which is generally the recommended&#xA;approach for security), and&lt;/li&gt;&#xA;&lt;li&gt;You need to write to the mounted file share.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;If you just need to read data from the share, then this is not a problem. If you need to write to it,&#xA;however, this really throws a wrench into your plans, and the documentation doesn&amp;rsquo;t tell you&#xA;how to work around it.&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-workaround&#34;&gt;The workaround&lt;/h2&gt;&#xA;&lt;p&gt;The workaround is described in more detail on &lt;a href=&#34;https://github.com/andyzhangx/Demo/blob/master/linux/azurefile/azurefile-mountoptions.md#set-mountoptions-in-static-provisioning-for-azure-file-support-from-v150&#34;&gt;this sample&lt;/a&gt;&#xA;by Andy Zhang.&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Create a new Kubernetes Persistent Volume for your Azure Files Share, and override the&#xA;default file and directory permissions for the file share.&lt;/p&gt;&#xA;&lt;div class=&#34;codeblock&#34;&gt;&#xA;  &lt;div class=&#34;codeblock__bar&#34;&gt;&#xA;    &lt;span class=&#34;codeblock__name&#34;&gt;yaml&lt;/span&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;apiVersion&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;v1&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;kind&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;PersistentVolume&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;metadata&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;pv-azurefile&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;spec&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;capacity&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;storage&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;5Gi&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;accessModes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;ReadWriteMany&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;azureFile&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;secretName&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;azure-secret&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;shareName&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;k8stest&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;readOnly&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;mountOptions&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;dir_mode=0777&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;file_mode=0777&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;uid=10000&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;gid=10000&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;mfsymlinks&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;nobrl&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&lt;/div&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Create a new Persistent Volume Claim for your service to request access to the&#xA;Persistent Volume created in the previous step&lt;/p&gt;&#xA;&lt;div class=&#34;codeblock&#34;&gt;&#xA;  &lt;div class=&#34;codeblock__bar&#34;&gt;&#xA;    &lt;span class=&#34;codeblock__name&#34;&gt;yaml&lt;/span&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;kind&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;PersistentVolumeClaim&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;apiVersion&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;v1&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;metadata&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;pvc-azurefile&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;spec&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;accessModes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;ReadWriteMany&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;mountOptions&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;resources&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;requests&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;storage&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;5Gi&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;storageClassName&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;volumeName&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;pv-azurefile  &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&lt;/div&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Create a volume for your pod out of the PVC.&lt;/p&gt;&#xA;&lt;div class=&#34;codeblock&#34;&gt;&#xA;  &lt;div class=&#34;codeblock__bar&#34;&gt;&#xA;    &lt;span class=&#34;codeblock__name&#34;&gt;yaml&lt;/span&gt;&#xA;  &lt;/div&gt;&#xA;  &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;volumes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;azurefile01&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;persistentVolumeClaim&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;claimName&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;pvc-azurefile&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&lt;/div&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Notice that the storage capacity is required on both the Persistent Volume and the Claim, and should be&#xA;equal or smaller to your Azure File Share size.&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-disadvantages&#34;&gt;The disadvantages&lt;/h2&gt;&#xA;&lt;p&gt;The documented workaround works just fine. However, the whole issue is frustrating on many levels.&lt;/p&gt;&#xA;&lt;p&gt;First of all, making it hard to follow the recommended practice of running containers&#xA;as non-privileged users makes no sense to me. This should be easy, and work right away without&#xA;having to result to these complicated steps.&lt;/p&gt;&#xA;&lt;p&gt;Second, the fact that the documentation doesn&amp;rsquo;t warn you about the issue, and doesn&amp;rsquo;t provide a link&#xA;to the solution, is very frustrating, and causes you to lose a few hours for no good reason.&lt;/p&gt;&#xA;&lt;p&gt;While I can sort of understand why restricting the default Unix permissions on the file share,&#xA;it seems to me this is just a very leaky abstraction, than on a platform like AKS doesn&amp;rsquo;t make&#xA;a lot of sense.&lt;/p&gt;&#xA;&lt;p&gt;The most frustrating aspect for me, however, is the fact that using Persistent Volumes forces you&#xA;to have to create those manually before you can deploy applications. If you just need&#xA;to reference a single Azure File Share, then it&amp;rsquo;s sort of OK, but if you need to reference&#xA;several, it quickly adds to a lot of work. You need to manually create at least 2 Kubernetes objects&#xA;(a Secret, and the Persistent Volume) for each share.&lt;/p&gt;&#xA;&lt;p&gt;I&amp;rsquo;m using &lt;a href=&#34;https://docs.helm.sh/&#34;&gt;Helm&lt;/a&gt; to deploy my application, and now I can&amp;rsquo;t simply&#xA;deploy the helm chart. I need to manually create stuff, ensure it&amp;rsquo;s correct, and only then can I&#xA;deploy it. This is just cumbersome.&lt;/p&gt;&#xA;&lt;p&gt;I wish you could just adjust default permissions directly when creating the volume, but alas,&#xA;apparently this is not possible with the Kubernetes model (or simply unsupported by the Azure Files plugin).&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; Posted some additional findings &lt;a href=&#34;https://winterdom.com/2018/07/26/azurefile-persistent-volumes-retain-issue/&#34;&gt;here&lt;/a&gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;getting-fixed&#34;&gt;Getting fixed!&lt;/h2&gt;&#xA;&lt;p&gt;&lt;strong&gt;Update 2018-11-15&lt;/strong&gt;: Looks like this issue is &lt;a href=&#34;https://github.com/kubernetes/kubernetes/pull/69854&#34;&gt;finally fixed&lt;/a&gt;&#xA;in Kubernetes &lt;code&gt;v1.11.4&lt;/code&gt; with the default permissions for Azure Files reverting to &lt;code&gt;0777&lt;/code&gt;.&#xA;Great news.&lt;/p&gt;&#xA;</description>
				<pubDate>Mon, 23 Jul 2018 00:00:00 +0000</pubDate>
				<link>https://winterdom.com/2018/07/23/aks-azure-files-permissions/</link>
				<guid isPermaLink="true">https://winterdom.com/2018/07/23/aks-azure-files-permissions/</guid>
			</item>
		
	</channel>
</rss>
