<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Development on SecopsMonkey</title>
    <link>https://secopsmonkey.com/tags/development/</link>
    <description>Recent content in Development on SecopsMonkey</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en-us</language>
    <lastBuildDate>Mon, 06 Apr 2026 11:41:19 -0400</lastBuildDate><atom:link href="https://secopsmonkey.com/tags/development/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Tool Library Announcement</title>
      <link>https://secopsmonkey.com/post/2026-04-06-tool-library-announcement/</link>
      <pubDate>Mon, 06 Apr 2026 11:41:19 -0400</pubDate>
      
      <guid>https://secopsmonkey.com/post/2026-04-06-tool-library-announcement/</guid>
      
        <description>&lt;p&gt;During a recent conversation someone pointed out that my &lt;a href=&#34;https://github.com/packs&#34;&gt;Github profile&lt;/a&gt; is somewhat dusty. Afterwards I took a public
view and dang, they&amp;rsquo;re absolutely right. Nearly all of my recent work has been restricted to private repos or owned by my
employers.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s fix that, shall we? Starting today I plan to go through all of the various scripts and applications I&amp;rsquo;ve written over
the years. For each script I&amp;rsquo;ll tidy them up enough for a public release and publish a blurb about each one.&lt;/p&gt;
&lt;p&gt;To kick off the process I&amp;rsquo;ve released &lt;a href=&#34;https://github.com/packs/entra_management/tree/main/Update-UserAttributes&#34;&gt;Update-UserAttributes&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;background&#34;&gt;Background&lt;/h2&gt;
&lt;p&gt;Like any good Internet recipe, here&amp;rsquo;s some context-flavored reminiscing.&lt;/p&gt;
&lt;p&gt;It was the halcyon days of late 2024 and I had recently taken over as technical owner of our Microsoft 365 environment. One
of my primary goals was to cleanup and modernize our technical practices. Previously, all Microsoft 365 administration had been
manual. The result was a lot of cruft, inconsistent data, and inefficient processes. One of the first steps of an overall
Identity Management Improvement project was to make sure our user data was accurate and trustworthy. This meant defining an
authoritative data source for user information and ensuring that the downstream systems, in this case Entra, were kept up to date.&lt;/p&gt;
&lt;p&gt;Since Human Resources used Paylocity as their HRIS, this meant setting up the integration for user attribute updates. The integration
supports automatic user creation, attribute updates, and account disabling. One hiccup, Paylocity is a purely event driven system.
Meaning, it had no way of correcting Entra directory information without individually modifying each and every person in Paylocity.&lt;/p&gt;
&lt;p&gt;Thus &lt;code&gt;Update-UserAttributes&lt;/code&gt; was born.&lt;/p&gt;
&lt;h2 id=&#34;the-intent&#34;&gt;The Intent&lt;/h2&gt;
&lt;p&gt;This script, &lt;code&gt;Update-UserAttributes.ps1&lt;/code&gt;, was designed to make bulk modifications to Entra user objects. In my use case, it was to
take an export from Paylocity and perform a reconciliation of directory information (Job Titles, Managers, Departments, etc). In
the general case, I wanted it to be useful as a generic user information management tool. Not every attribute is owned by HR, and this
would give us the opportunity to manage them as needed.&lt;/p&gt;
&lt;h2 id=&#34;usage&#34;&gt;Usage&lt;/h2&gt;
&lt;p&gt;The best use case is bulk-updating multiple attributes across multiple users simultaneously. To do this, create a CSV file using the
following format. Something important to note is that the header name must match the property name used by the &lt;a href=&#34;https://learn.microsoft.com/en-us/powershell/module/microsoft.graph.users/update-mguser?view=graph-powershell-1.0&#34;&gt;&lt;code&gt;Update-MgUser&lt;/code&gt;&lt;/a&gt; cmdlet.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-csv&#34; data-lang=&#34;csv&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;UserId&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;jobTitle&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;department&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;Manager&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;
&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;s&#34;&gt;user1@domain.com&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;Sales Director&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;Sales&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;manager@domain.com&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;
&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;s&#34;&gt;user2@domain.com&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;Engineer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;Development&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;manager@domain.com&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Once you create the CSV you can make the upstream changes by passing it on the command line&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-powershell&#34; data-lang=&#34;powershell&#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;&lt;span class=&#34;nb&#34;&gt;Update-UserAttributes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;py&#34;&gt;ps1&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;-CSVFilePath&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;.\&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;userlist&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;csv&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;More details can be found in the script&amp;rsquo;s README and Help output.&lt;/p&gt;
&lt;h2 id=&#34;caveats&#34;&gt;Caveats&lt;/h2&gt;
&lt;p&gt;There&amp;rsquo;s definitely room for improvement. For example:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Add &lt;code&gt;-WhatIf&lt;/code&gt; support&lt;/li&gt;
&lt;li&gt;Move Error reporting to &lt;code&gt;Write-Error&lt;/code&gt; and add gobs more&lt;/li&gt;
&lt;li&gt;Actually implement exception handling&lt;/li&gt;
&lt;li&gt;Implement output logging sufficient to be auditable&lt;/li&gt;
&lt;li&gt;Validate CSV input&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Please don&amp;rsquo;t be shy with your feature and/or pull requests. My main reason for publishing is to make it available in the hope
that someone else finds it useful.&lt;/p&gt;
</description>
      
    </item>
    
  </channel>
</rss>
