Greg Siekman

Greg Siekman

Automation & Systems Engineer

Using code and digital tools to automate tasks and make work easier.

Open to opportunities in education technology and beyond.

About

I started my development career inhereting a web extension that automated tasks in Canvas LMS. Working on that laid the ground work for how I understand the capacity of software to make work easier. I continue to carry that ethos in my work. Whether I’m setting up auto-deploy practices or implementing documentation-as-code, I try to limit the amount of tedious work involved in the work getting done.

I fundamentally believe technology should be centered on user value— a model that, by-and-large, tech companies have turned away from.

Tools

TypeScriptGitHub ActionsPythonREST APIWeb Extension DevDocumentation

Experiences

2025

Solution Developer · Unity Environmental University

Expanded on an internal browser extension and suite of automation tools used to manage 130+ courses, focusing on eliminating manual friction for educators and staff.

  • Extended TypeScript and React automation pipeline to automate boilerplate content and settings across multiple academic levels
  • Implemented robust CI/CD pipeline, replacing manual publication scripts with automated testing, PR-based preview builds, and GPO-based device updates.
  • Built one-click workflow tools that bridge internal data sources (Canvas, Salesforce, and Trello)
  • Led the rollout of a version-controlled "documentation-as-code" platform to keep documentation with the code
  • Developed data-driven dashboards to surface insights from ticket feedback and course logs, shifting curriculum design toward evidence-based improvements
  • TypeScript
  • React
  • Python
  • Node
  • Zustand

2023

Learning Technology Support Specialist · Unity Environmental University

As inaugural LTSS, set procudes for ticket handling, video production, LTI tool audits, and Canvas maintenance.

  • Acted as the primary technical authority for Canvas LMS, resolving Tier 1 & 2 issues regarding course design, rubrics, and platform settings
  • Defined and implemented protocols for ticket lifecycle management, LTI tool compliance audits, and academic video production
  • Collaborated with learning design teams to translate complex technical requirements into actionable, user-friendly solutions
  • Served as the bridge between end-user frustrations and technical implementation, identifying recurring pain points that informed course development
  • Canvas LMS
  • Salesforce
  • LTI
  • HTML/CSS

2022

Technology Integrator/IT Support Specialist · MSAD #58

Provided hands-on technical support and infrastructure maintenance for a K-12 school district, ensuring reliable hardware and software environments for faculty and students.

  • Executed large-scale summer device rollout and fleet maintenance across four campuses, maintaining operational standards through complex logistical challenges
  • Resolved Tier 1 support tickets, covering end-user identity management, software troubleshooting, and classroom A/V issues
  • Implemented physical network infrastructure upgrades, including the installation and cabling of security cameras and wireless access points across the district
  • Evaluated emerging educational technology tools to ensure alignment with K-12 curriculum and district-wide security standards
  • Ticketing
  • Jamf
  • macOS
  • Clever

Blog

Canvas Test Course Reset Tool

2026-07-23

Screenshot of a dark screen showing the Test Course Reset Utility. Buttons for Configuration, Default Course Reset, Custom Course Reset and Quit are stacked in the middle.

In the process of building a personal ops tool to centralize a bunch of common work tasks, I created a process that resets my test courses. After using it for a bit, I realized it was something others might benefit from, so I worked on making a standalone version of it. The original verrsion was built in JavaScript because my ops tool is using Electron to run as an app. But I didn't want a heavy Electron setup for this course reset utility, so I opted to port the functions over to Python and wrap it all in a Textual UI. Needing the UI to at least be moderately pleasant to look at meant that Textual was a little heavier than I originally anticipated, but it came together nonetheless! It's packaged with pyinstaller, so it runs as a standalone file, which I thought was more accessible than needing to run a py file in the terminal. The tool is designed with Canvas admins in mind. When you open it up, you have the following buttons: 'Configuration,' 'Default Course Reset,' 'Custom Course Reset.' The configuration screen prompts you to enter their Canvas subdomain, an access key, the target course ID and the source course ID. Each step gives brief instructions on what you need to give it. subdomain is looking for something like "myschool" in "myschool.instructure.com." If you give it the full url, it will snip it back to just the subdomain. It does basic data validation for the other rows, checking that the access key is long enough to theoretically be an access key, and that the two course IDs are just numbers. Once your configuration is set up, you can user Default Course Reset to reset the course you just set with the other course's materials. The screen will throw a loading indicator while it waits for the response from Canvas. Once the response comes in, you get a confirmation and a command to click the link to go to the content migrations page. The reason that page was chosen as the landing page in Canvas is because content migrations take a while, and that screen has an indicator of the progress of the migration. I contemplated adding a progress bar in the tool, but opted against it as in introduced extra API calls. If you select Custom Course Search, it gives you a chance to select either your default course or a different course for both the target course and the source course. If you choose to use a different course, you can either enter the ID or the course name. The course name will run a search of courses available and give you a list to select from. If you're doing that for the target course and the course you selected doesn't have test in the name, it will ask you to confirm this is the course you wanted. Because this was designed around being a test course resetter, this felt like a reasonable thing to add. You can reset non-test courses if you want, but that's at your own risk. Once you've selected your custom courses, it runs just like Default Course Reset. The coolest thing about this tool is that it handles a weird Canvas quirk gracefully. In Canvas, when you reset a course, you are essentially deleting the old course and creating a new one with it's basic information. The id for the course changes. This can cause problems in automation, because you have to be careful about hard coding the id for a course you plan to reset more than once. This utility takes the returned course information from the API call when you reset the course, and replaces the old target course id in your config with the new one, so your config is always pointed at the newest version of your test course. It had a bug in it at first where it would replace the ID in your config at any time you ran a course reset, which was bad because it would replace your default target course when you ran a custom course search. To handle this, I added a check in the process:  is_custom_course = (    target_course_id is not None    and target_course_id != default_env_course ) if not is_custom_course:   set_key(env_path, "TARGET_COURSE_ID", str(new_course_id))   os.environ["TARGET_COURSE_ID"] = str(new_course_id) Because the code only passes in a target_course_id when you're using the custom reset with a custom target course, this blocks it from overriding your config if you aren't using your default course. It's clever because it checks if you chose the custom course option but still chose your default course. Overall, it's meant to be a utility tool, not necessarily a consumer facing tool. But I hope people will get use out of it. It's certainly rough around the edge and has potential bug that I will continue to iron out as I find them or they get reported to me.

Automating Sprint Summary Reports

2026-07-23

For a few months, I was tasked with leading the sprint process on our team. This including running our debrief and planning meetings, and developing a summary write-up of what happened in the previous sprint for leadership on other teams. Despite leading the meetings, I wasn't always as sharply abreast on the specific details of what people were working on, which made it a bit tough to write the sprint summary from memory. Every two weeks, I could've opened up our Trello board and scrolled through cards and lists to write up this summary. That felt slow and tedious to me. So, I opened up me IDE and built a Python script that automated a little bit of this work. Using the Trello API, I pulled cards from the relevant lists in our board. The script dumped the cards into a csv, generated a data file, and created a template markdown file to draft into. The data file is my favorite part of the project. It attempts to establish keywords from the sprint by counting words from the titles and descriptions of cards. For example, one of the data files has a keyword "Field." It was in five cards, and then below the keyword, it lists the titles of each card. This section used a rudimentary filter list to filter out common words that weren't useful descriptions of the work being done(people's names.) It also attempts to use a frequency filter to determine relevancy. A word that is in 9 cards, for example, is assumed to be a common word and not a descriptive word of the work being done. The data sheet does this for completed items, and then in a separate list for active and planned items. At the bottom of the data file is then a 'Quick Scan Index' of the cards, which is just the card titles in a text list corresponding with the lists in Trello. With those files on hand, I was able to write the sprint summaries faster. I could use keywords to prompt what we got done and what we planned to get done. I could scan the cards to catch anything I missed. Once I was done drafting, I could run a formatter script that converted the markdown file into HTML; as that was a friendlier format for Outlook, and copied it to my clipboard. If I were going to pick this project up again, I would put more work into that formatter script. Outlook was a little fidgety with it's formatting preferences, and I always had to spend some time formatting it manually. I could also do more work on how it determines what a keyword is and surfacing relevant information from the cards.

Porting my Personal Website from Astro

2026-07-16

A picture of Greg Siekman's personal website.

A few years ago, I built my website: siekmang.com. I wrote it with vanilla HTML, CSS and JavaScript because that is what I knew at the time. Since then, I've been battling with layout shift on first load of the site. Recently, when I decided to build out my developer site, I decided to use Astro. I was captivated by the speed at which pages load. After finishing that site(or this site, I guess,) I was intrigued if I could get similar performance on my personal site by switching over to Astro. I thought it was going to be a whole refactoring journey, building out components and layouts, but I was able to drop my existing site into an Astro project, change .html to .astro, move a few things around to the right places in the directory and have a functioning site. Better yet, the performance gains were already clear! The biggest issue I had in the port over had to do with my use of document selectors in some of my JavaScript. The solution was to use script tags for my existing JS files. Another thing that took some reworking was my header generator. What it currently does is pull the slug from the page, if that slug is privacy, sets the header to Privacy Policy, and if it's not, defaults the header to my name. How I was doing that in my old code: var page = window.location.pathname.split("/").pop(); In the Astro build of the site, that kept turning up blank. After a little bit of troubleshooting, I figured out that it was easier to just peel away any back-slashes and just leave the slug using removeAll. That got the header generator working. At that point, I merged the astro code into main, and Cloudflare Pages built and shipped it. As a side note, I really gained an appreciation for Cloudflare Pages in the process. It's integration with GitHub is so effortless and it built every push in a preview environment, which helped increase my confidence that what I was going to eventually ship to main was going to work. Another tip: astro dev is great for visual changes on your site, but I got tricked a few times by things not working in that environment. astro build and astro preview swooped in to save the day on that. I still plan to move toward components and layouts for that site eventually, as well as replacing my bloated css with Tailwind, but having the performance upgrades from the jump with Astro was a really nice quality of life upgrade for my site.

View Full Blog Archive