<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Samuel Kling</title>
    <description>The latest articles on Forem by Samuel Kling (@samklingdev).</description>
    <link>https://forem.com/samklingdev</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F578788%2F61195a74-35c5-4328-81a9-5304d2f8f1b0.jpeg</url>
      <title>Forem: Samuel Kling</title>
      <link>https://forem.com/samklingdev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/samklingdev"/>
    <language>en</language>
    <item>
      <title>Unleashing Real-Time Magic with Server-Sent Events (SSE), Go, and HTMX</title>
      <dc:creator>Samuel Kling</dc:creator>
      <pubDate>Wed, 04 Oct 2023 19:43:44 +0000</pubDate>
      <link>https://forem.com/samklingdev/unleashing-real-time-magic-with-server-sent-events-sse-go-and-htmx-20c6</link>
      <guid>https://forem.com/samklingdev/unleashing-real-time-magic-with-server-sent-events-sse-go-and-htmx-20c6</guid>
      <description>&lt;p&gt;Hey there fellow developers, today, we're diving into the world of real-time web applications using Server-Sent Events (SSE), the Go programming language, and the incredible HTMX library. Buckle up because this combo is going to take your web development game to a whole new level!&lt;/p&gt;

&lt;h2&gt;
  
  
  The Power of Server-Sent Events (SSE)
&lt;/h2&gt;

&lt;p&gt;Let's start with SSE, shall we? SSE is like having a direct hotline from your server to your web browser, allowing real-time updates without all the fuss and complexity of other methods. Here's why SSE rocks:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Real-Time Updates Made Easy&lt;/strong&gt;: SSE allows your server to push updates to the client over a single, long-lived HTTP connection. Say goodbye to endless polling and WebSocket setup headaches.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lightweight and Nimble&lt;/strong&gt;: SSE uses a lightweight, text-based format (usually JSON), keeping things efficient. No unnecessary overhead here, which is excellent news for your bandwidth and server resources.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Browser Love&lt;/strong&gt;: SSE enjoys wide browser support. Chrome, Firefox, Safari, Edge—you name it—they're all onboard, making SSE accessible to the masses.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Simplicity Rules&lt;/strong&gt;: SSE is easy to implement on both the server and client sides. It's all about standard HTTP requests and event listeners, making integration into existing apps a breeze.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to Unleash SSE
&lt;/h2&gt;

&lt;p&gt;Now that you're pumped about SSE, let's talk about when to use it. SSE is your go-to for real-time updates without the complications. Here's when to drop the SSE magic:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Live Notifications&lt;/strong&gt;: Imagine sending live notifications to users—new messages, likes, comments—SSE makes it a walk in the park.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Monitoring Mastery&lt;/strong&gt;: Need real-time server metrics or application health updates? SSE's got your back.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Collaboration Central&lt;/strong&gt;: In collaborative apps like Google Docs, SSE syncs changes from multiple users in real time, creating a seamless experience.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Gaming Galore&lt;/strong&gt;: SSE can keep your online game's state and player actions in sync, delivering a gaming experience that keeps players glued to their screens.&lt;/p&gt;

&lt;h2&gt;
  
  
  Go and HTMX: A Dynamic Duo for SSE
&lt;/h2&gt;

&lt;p&gt;Now, here's where the real magic happens—pairing SSE with Go and HTMX. Check this out:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Go&lt;/strong&gt;: Go, the superhero of languages, steps up to the plate on the server side. With Go's goroutines, managing those long-lived connections is a breeze. Plus, it comes equipped with an HTTP server in its standard library, the perfect match for serving SSE events.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// main.go&lt;/span&gt;
&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"context"&lt;/span&gt;
    &lt;span class="s"&gt;"fmt"&lt;/span&gt;
    &lt;span class="s"&gt;"net/http"&lt;/span&gt;
    &lt;span class="s"&gt;"time"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HandleFunc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResponseWriter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ServeFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"sse.html"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HandleFunc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/events"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResponseWriter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c"&gt;// Set headers for SSE&lt;/span&gt;
        &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Header&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Content-Type"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"text/event-stream"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Header&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Cache-Control"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"no-cache"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Header&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Connection"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"keep-alive"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c"&gt;// Create a channel to send data&lt;/span&gt;
        &lt;span class="n"&gt;dataCh&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;chan&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c"&gt;// Create a context for handling client disconnection&lt;/span&gt;
        &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cancel&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WithCancel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;cancel&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="c"&gt;// Send data to the client&lt;/span&gt;
        &lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;dataCh&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"data: %s&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Flusher&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Flush&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}()&lt;/span&gt;

        &lt;span class="c"&gt;// Simulate sending data periodically&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;dataCh&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TimeOnly&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Second&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Server ready: http://localhost:%s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;PORT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ListenAndServe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;":"&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;PORT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;HTMX&lt;/strong&gt;: HTMX, our frontend ally, adds dynamic, AJAX-driven behavior to your web pages. The beauty is in its simplicity. No more writing complex WebSocket code. With HTMX, you can integrate SSE seamlessly into your HTML without breaking a sweat.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- sse.html --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Go SSE&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;

        &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://unpkg.com/htmx.org@1.9.6"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://unpkg.com/htmx.org/dist/ext/sse.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;SSE Example with HTMX&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;hx-ext=&lt;/span&gt;&lt;span class="s"&gt;"sse"&lt;/span&gt; &lt;span class="na"&gt;sse-connect=&lt;/span&gt;&lt;span class="s"&gt;"/events"&lt;/span&gt; &lt;span class="na"&gt;sse-swap=&lt;/span&gt;&lt;span class="s"&gt;"message"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            Contents of this box will be updated in real time with every SSE message received from the chatroom.
        &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Frontend Simplified&lt;/strong&gt;: HTMX abstracts the complexity of SSE, meaning you can focus on your app's logic instead of wrestling with WebSockets and custom event handling.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scalability Mastered&lt;/strong&gt;: Go's performance and concurrency capabilities make it a juggernaut for handling countless SSE connections simultaneously. Whether you're serving updates to a few users or an army, Go's got it covered.&lt;/p&gt;

&lt;p&gt;In a nutshell, SSE with Go and HTMX is like a match made in developer heaven. It simplifies development and delivers a mind-blowing real-time experience to your users. Whether you're building a chat app, a monitoring dashboard, or a collaborative tool, this combo will take your app to the next level.&lt;/p&gt;

&lt;p&gt;Ready to unleash the power of SSE with Go and HTMX? Let's get coding and create web applications that redefine real-time interactivity.&lt;/p&gt;

&lt;p&gt;Remember, the magic happens when you code, so go forth, fellow devs, and make the web a real-time wonderland!&lt;/p&gt;

&lt;p&gt;And that's a wrap, folks! Until next time, keep coding, keep innovating, and stay legendary! 🚀✨&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Use Windows Data Protection API with Python for handling credentials.</title>
      <dc:creator>Samuel Kling</dc:creator>
      <pubDate>Wed, 12 Jan 2022 21:01:15 +0000</pubDate>
      <link>https://forem.com/samklingdev/use-windows-data-protection-api-with-python-for-handling-credentials-5d4j</link>
      <guid>https://forem.com/samklingdev/use-windows-data-protection-api-with-python-for-handling-credentials-5d4j</guid>
      <description>&lt;p&gt;As a IT Tech, I do alot of automation scripting, both in Powershell and Python against various systems and Ive never liked how I had to handle credentials in scripts.&lt;/p&gt;

&lt;p&gt;I dont want to have any credentials or API-keys in plain text on a server that others have access to, so I saved it as environment variables like most of us do.&lt;br&gt;
That works fine, but when you have hundreds of variables for email accounts, API-keys and SFTP accounts it can get messy. &lt;/p&gt;

&lt;p&gt;When I looked for a better solution I found this gem for Powershell!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;All commands below are run from PowerShell.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Prompt for Username and Password and save to file&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;PS C:&lt;span class="se"&gt;\&amp;gt;&lt;/span&gt; Get-Credential | Export-Clixml &lt;span class="nt"&gt;-Path&lt;/span&gt; &lt;span class="s2"&gt;"cred.xml"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Import the credentials from a file to a PSCredential object.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;PS C:&lt;span class="se"&gt;\&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$cred&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; Import-Clixml &lt;span class="nt"&gt;-Path&lt;/span&gt; &lt;span class="s2"&gt;"cred.xml"&lt;/span&gt;
PS C:&lt;span class="se"&gt;\&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$cred&lt;/span&gt;

UserName                     Password
&lt;span class="nt"&gt;--------&lt;/span&gt;                     &lt;span class="nt"&gt;--------&lt;/span&gt;
samkling System.Security.SecureString
PS C:&lt;span class="se"&gt;\&amp;gt;&lt;/span&gt; ConvertFrom-SecureString &lt;span class="nv"&gt;$cred&lt;/span&gt;.password &lt;span class="nt"&gt;-AsPlainText&lt;/span&gt;

password
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;ConvertFrom-SecureString -AsPlainText requires PowerShell 7.0.&lt;br&gt;
Export-Clixml only exports encrypted credentials on Windows.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/export-clixml?view=powershell-7.1"&gt;Export-Clixml&lt;/a&gt;  cmdlet encrypts credential objects by using the &lt;a href="https://docs.microsoft.com/en-us/previous-versions/windows/apps/hh464970(v=win.10)"&gt;Windows Data Protection API&lt;/a&gt; . The encryption ensures that only your user account on only that computer can decrypt the contents of the credential object. The exported CLIXML file can't be used on a different computer or by a different user.&lt;/p&gt;

&lt;p&gt;I now store the credentials neatly, and secure in a credentials.xml file in the same directory as the actual script. Anyone can access the credential file and the script, but they wont be able to decrypt&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
├─ company1-sftp-script
│  ├─ download-files-newer-than-1-day.ps1
│  └─ credentials.xml
└─ company2-sftp-script
   ├─ download-files-newer-than-1-day.ps1
   └─ credentials.xml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  How can we use the same method in Python?
&lt;/h2&gt;

&lt;p&gt;Sadly there is no Export-Clixml/Import-Clixml equalent for Python so we will have to build it ourselves.&lt;/p&gt;

&lt;p&gt;First we need to have access Windows Data Protection API. There are several ways, I use &lt;a href="https://pypi.org/project/pywin32/"&gt;pywin32&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;PS C:&lt;span class="se"&gt;\&amp;gt;&lt;/span&gt; pip &lt;span class="nb"&gt;install &lt;/span&gt;pywin32
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then we need to create two files in python.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# export_clixml.py
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;win32crypt&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;binascii&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;sys&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;export_clixml&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="c1"&gt;# encrypt the password with DPAPI.
&lt;/span&gt;    &lt;span class="n"&gt;crypted_password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;win32crypt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CryptProtectData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"utf-16-le"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Do some magic to return the password in the exact same format as if you would use Powershell.
&lt;/span&gt;    &lt;span class="n"&gt;password_secure_string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;binascii&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hexlify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;crypted_password&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c1"&gt;# Use the same xml format as for powershells Export-Clixml, just replace values for username and password.
&lt;/span&gt;    &lt;span class="n"&gt;xml&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s"&gt;"""&amp;lt;Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"&amp;gt;
    &amp;lt;Obj RefId="0"&amp;gt;
        &amp;lt;TN RefId="0"&amp;gt;
        &amp;lt;T&amp;gt;System.Management.Automation.PSCredential&amp;lt;/T&amp;gt;
        &amp;lt;T&amp;gt;System.Object&amp;lt;/T&amp;gt;
        &amp;lt;/TN&amp;gt;
        &amp;lt;ToString&amp;gt;System.Management.Automation.PSCredential&amp;lt;/ToString&amp;gt;
        &amp;lt;Props&amp;gt;
        &amp;lt;S N="UserName"&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;/S&amp;gt;
        &amp;lt;SS N="Password"&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;password_secure_string&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;/SS&amp;gt;
        &amp;lt;/Props&amp;gt;
    &amp;lt;/Obj&amp;gt;
    &amp;lt;/Objs&amp;gt;"""&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;xml&lt;/span&gt;


&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"__main__"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# Dont do this, It's just so that we can pipe the output to file to mimic the powershells version.
&lt;/span&gt;        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;export_clixml&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# import_clixml.py
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;win32crypt&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;binascii&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;sys&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;import_clixml&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"r"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;encoding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"utf-8"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;xml&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="c1"&gt;# Extract username and password from the XML since thats all we care about.
&lt;/span&gt;        &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;xml&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'&amp;lt;S N="UserName"&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&amp;lt;/S&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;password_secure_string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;xml&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'&amp;lt;SS N="Password"&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&amp;lt;/SS&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

        &lt;span class="c1"&gt;# CryptUnprotectDate returns two values, description and the password, 
&lt;/span&gt;        &lt;span class="c1"&gt;# we dont care about the description, so we use _ as variable name.
&lt;/span&gt;        &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;decrypted_password_string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;win32crypt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CryptUnprotectData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;binascii&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unhexlify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;password_secure_string&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;decrypted_password_string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;


&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"__main__"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# We use sys args just to mimic the powershell version.
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;import_clixml&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lets try it out and see if we can use Powershells Export-Clixml and Pythons Import-Clixml.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;PS C:&lt;span class="se"&gt;\&amp;gt;&lt;/span&gt; Get-Credential | Export-Clixml &lt;span class="nt"&gt;-Path&lt;/span&gt; powershell_cred.xml 

PowerShell credential request
Enter your credentials.
User: samkling
Password: password

PS C:&lt;span class="se"&gt;\&amp;gt;&lt;/span&gt; py .&lt;span class="se"&gt;\I&lt;/span&gt;mport_Clixml.py .&lt;span class="se"&gt;\p&lt;/span&gt;owershell_cred.xml

samkling, password
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sweet, so it returned the correct username and password decrypted.&lt;/p&gt;

&lt;p&gt;What about the other way around? From Python to Powershell?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;PS C:&lt;span class="se"&gt;\&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$cred&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; Import-Clixml &lt;span class="nt"&gt;-Path&lt;/span&gt; .&lt;span class="se"&gt;\p&lt;/span&gt;ython_cred.xml
PS C:&lt;span class="se"&gt;\&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$cred&lt;/span&gt;

UserName                     Password
&lt;span class="nt"&gt;--------&lt;/span&gt;                     &lt;span class="nt"&gt;--------&lt;/span&gt;
samkling System.Security.SecureString

PS C:&lt;span class="se"&gt;\&amp;gt;&lt;/span&gt; ConvertFrom-SecureString &lt;span class="nv"&gt;$cred&lt;/span&gt;.password &lt;span class="nt"&gt;-AsPlainText&lt;/span&gt;

password
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;How cool is that!&lt;/p&gt;

</description>
      <category>python</category>
      <category>powershell</category>
    </item>
  </channel>
</rss>
