Jekyll2019-09-14T15:29:15-05:00https://www.thatexactmike.com/feed.xmlThat Exact MikeAdventures in DevOps, PowerShell, Mergers & Acquisitions, Identity, Cloud, Azure, Exchange, Active Directory, Office 365 and more.Mike CampbellTest-ForPwnedPassword2019-04-08T00:00:00-05:002019-04-08T00:00:00-05:00https://www.thatexactmike.com/Test-ForPwnedPassword<p>Test-ForPwnedPassword is a function to check the API hosted at api.pwnedpasswords.com for passwords that have been found in a data breach.</p>
<h2 id="usage">usage</h2>
<h3 id="example-1---securestring-password">example 1 - SecureString Password</h3>
<p>Tests the submitted password (converted to the SHA1 hash value) to see if it is present in the API’s data set as a breached password.</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$Password</span> <span class="o">=</span> <span class="nb">Read-Host</span> -AsSecureString
Test-ForPwnedPassword -Password <span class="nv">$Password</span>
</code></pre></div></div>
<h4 id="output">Output</h4>
<p>This example will produce a boolean output. True if the password exists in the APIs breached password database, false if it does not.</p>
<h3 id="example-2---hash-with">example 2 - Hash with</h3>
<p>Tests the password hash for the password ‘Micro$oft’ to see if it is present in the API’s data set as a breached password and specifies the IncludeInstanceCount parameter.</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Test-ForPwnedPassword -Hash 9cd277f71f1a9d77eccb441836bdea6f1b5c2685 -IncludeInstanceCount
</code></pre></div></div>
<h4 id="output-1">Output</h4>
<p>This example will produce a PSCustomObject output with attributes FoundInPwnedPasswordsAPI and InstanceCount as shown below:</p>
<p><img src="https://www.thatexactmike.com/assets/images/TestForPwnedPasswordObjectOutput.png" alt="no-alignment" /></p>
<h3 id="example-3---password-prompting">example 3 - Password Prompting</h3>
<p>Prompts for a password and tests the password hash to see if it is present in the API’s data set as a breached password.</p>
<p class="notice--warning">Don’t use this syntax if you are remoting to a non-windows machine due to <a href="https://github.com/PowerShell/PowerShell/issues/7239">problems with SecureString being passed between sessions</a>.</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Test-ForPwnedPassword
</code></pre></div></div>
<h3 id="using-the-pipeline">using the pipeline</h3>
<p>You can pipe one or more securestring or string objects into Test-ForPwnedPassword. Strings will bind to the Hash parameter and SecureStrings will bind to the Password parameter.</p>
<h2 id="background">background</h2>
<p>I had previously been aware of and used the email address lookup tool over at <a href="https://haveibeenpwned.com/">Troy Hunt’s haveibeenpwned</a> site, but I had not noticed the compromised (pwned) passwords lookup that was also available there. That all changed while I was listening to a recent episode of <a href="http://www.microsoftcloudshow.com/podcast/Episodes/296-have-i-been-pwned-an-interview-with-troy-hunt">Microsoft Cloud Show</a>. I highly recommend this podcast if you are into Microsoft Cloud technologies. I found out that <a href="https://twitter.com/troyhunt">Troy</a> released a v3 of the pwned passwords API <a href="https://www.troyhunt.com/pwned-passwords-v3-is-now-live/">in July 2018</a> so I went looking for any existing PowerShell function to use for automating these lookups. That search yielded the <a href="https://sqldbawithabeard.com/2017/08/09/using-powershell-to-check-if-your-password-has-been-in-a-breach/">Get-PwnedPassword</a> function from <a href="https://robsewell.info/">SQLDBAwithTheBeard</a> which uses one of the older APIs. The v3 API documentation is <a href="https://haveibeenpwned.com/API/v2#PwnedPasswords">here</a>.</p>
<h2 id="function-details">function details</h2>
<p>I started from the Get-PwnedPassword function and modified it to use the v3 API and to use the Test verb and by default to just return True if the password submitted is found in the API records and False if it is not found. Additionally, I added a parameter IncludedInstanceCount to return an object with the following attributes:</p>
<ul>
<li>FoundInPwnedPasswordsAPI: returns a boolean value, true if found, false if not</li>
<li>InstanceCount: returns the count that is returned by the API for how many times the password is found in the API dataset.</li>
</ul>
<p>I also preserved the Hash parameter (and parameter set) from Rob Sewell’s original Get-PwnedPassword function so that you can, if you wish, submit your own hashed values to avoid having Test-ForPwnedPassword have access to the actual password value. If you do use the Password parameter (and parameter set) the password must be submitted as a secure string (or you will be prompted for a secure string if you use the function interactively without providing a value to the Password parameter) and the password is converted to plain text in memore to be hashed by private function Get-StringHash (code for Get-StringHash is in the Begin block of the function). In either case, only the first 5 characters of the hash are sent to the remote API using Invoke-RestMethod.</p>
<p>After writing this function and while preparing this blog post, I also found a <a href="http://psfredrik.chiloma.com/author/frekac/">Test-PwnedPassword function by Fredrik Kacsmarck</a>. Fredrik and I took very similar approaches to this functionality with the main difference being Test-ForPwnedPassword just returns a boolean value by default and Test-ForPwnedPassword allowing for you to produce/provide your own hashes in some way of your choosing.</p>
<h2 id="powershellgallery">PowerShellGallery</h2>
<p>It’s available <a href="https://www.powershellgallery.com/packages/Test-ForPwnedPassword/0.1.0">here</a> in the PowerShell gallery and can be installed by running the following in a PowerShell session that has the PowerShellGet Module installed:</p>
<pre><code class="language-Powershell">Install-Script -Name Test-ForPwnedPassword
</code></pre>
<h2 id="feedback">feedback</h2>
<p>Any improvements to this function that you might recommend? I’m considering developing a few other functions to take advantage of the other APIs available at haveibeenpwned.com. Would you have a use for that?</p>
<h2 id="code">code</h2>
<p><a href="https://github.com/exactmike/Profile/blob/c5e2c775f4eba8a659521bb06069154dc35b086d/functions/Test-ForPwnedPassword.ps1#L45">Source Code</a> is currently hosted in my profile module which I use for personal powershell functions that I load with my powershell profile. Contributions are welcome!</p>Mike CampbellFunction to check for a password that is available in the compromised password dataset over at https://api.pwnedpasswords.com.Get-DateSeries2019-02-27T00:00:00-06:002019-04-01T00:00:00-05:00https://www.thatexactmike.com/Get-DateSeries<h1 id="background">background</h1>
<p>I needed to schedule a series of meetings and wanted to know/review all the dates and times before sending the invitation. Outlook, as far as I know, doesn’t make this very easy to do, and I can imagine many other scenarios (custom timers or Pomodoro timers?) where someone might want to use this.</p>
<h1 id="published-in-powershellgallery">Published in PowerShellGallery</h1>
<p>After writing this I decided to use it as my opportunity to publish my first content into the PowerShell Gallery. It’s available <a href="https://www.powershellgallery.com/packages/Get-DateSeries/">here</a> and can be installed by running the following in a PowerShell session that has the PowerShellGet Module installed:</p>
<pre><code class="language-Powershell">Install-Script -Name Get-DateSeries
</code></pre>
<h1 id="feedback">feedback</h1>
<p>I haven’t searched a lot yet, but is there an existing community module that does other interesting things with DateTime? Would love to contribute this there if so. <a href="https://github.com/jdhitsolutions/PSCalendar">Anyone using PSCalendar?</a></p>
<h1 id="code">code</h1>
<noscript><pre>400: Invalid request
</pre></noscript>
<script src="https://gist.github.com/517c5005319952c785a191c6143ca467.js?file=Get-DateSeries.ps1"> </script>Mike Campbellgets a series of datetime objects for a specified start, interval, interval units, and instance limit.ConvertTo-AzureADOnlyUser2018-10-24T00:00:00-05:002018-11-02T00:00:00-05:00https://www.thatexactmike.com/ConvertTo-AzureADOnlyUser<h1 id="background">background</h1>
<p>Unfortunately, there is not really a ConvertTo-AzureADOnlyUser (sometimes called a Cloud Only user) function in the Azure AD module nor the MSOnline module. But, at least for now, it is possible to do a conversion of an existing synchronized user (from an on premises directory) to a cloud only user that no longer depends on the on premises directory for it’s identity and attributes.</p>
<p>In my situation I had several mailboxes still hosted in an on premises Exchange environment and finally got around to migrating them. For these mailboxes, I no longer wanted them to have any on premises dependencies so I needed to convert them to cloud only, Azure AD only user objects.</p>
<p class="notice--danger">Warning/Danger: As far as I can tell there is not an ‘officially supported’ way of doing this conversion, and the process I’m documenting here is not supported as far as I know. The only way that I was able to do it uses the MSOnline module which is headed for retirement at some point. However, the following procedure worked for me as of this posting. It seems the official way to do this might be to delete the user, then create a new cloud only object and then connect orphaned/disconnected assets (like Exchange Mailbox and Onedrive?) to the newly created user object.</p>
<h1 id="steps">steps</h1>
<ul>
<li>Move the mailbox(es) to Exhange Online
<ul>
<li>I used a hybrid move and used both standalone move request(s) and a migration batch to move different mailboxes during this operation. Both worked fine.</li>
</ul>
</li>
<li>Remove the user object from synchronization with Azure AD.
<ul>
<li>warning: this will disrupt your ability to access the mailbox and may disrupt (briefly) mail flow to the mailbox</li>
<li>options:
<ul>
<li>delete the user from AD</li>
<li>modify the sync scope to exclude an OU where the object resides in AD (this is what I did)</li>
</ul>
</li>
</ul>
</li>
<li>Restore the user object from the Azure AD recycle bin
<ul>
<li>use Restore-Msoluser or Restore-AzureADMSDeletedDirectoryObject</li>
<li>use the AzureAD or Microsoft 365 portal</li>
</ul>
</li>
<li>Re-apply a license to the user object for the workload(s) you intend to keep using in Azure AD / Microsoft 365</li>
<li>
<p>Set the ImmutableID of the object to be a blank string. Do NOT set it to $null. That will seem to work, but it will silently fail (no errors, no warnings.) I tried using the Azure AD module to do this but a blank string fails with that module and $null fails silently.</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="nb">Set</span>-MSOLUser -UserPrincipalName <span class="o">[</span>the upn of your <span class="kt">object</span><span class="o">]</span> -ImmutableID <span class="s1">''</span>
</code></pre></div> </div>
</li>
</ul>
<h1 id="feedback">feedback</h1>
<p>Do you know a better or supported way to accomplish conversion of a user to an AzureAD Only user?</p>Mike Campbellconverting a user that was connected with on premises Active Directory to be a stand alone cloud / Azure AD userShow-One2018-10-19T00:00:00-05:002018-10-19T00:00:00-05:00https://www.thatexactmike.com/Show-One<h1 id="background">background</h1>
<p>I sometimes find myself needing to examine individual powershell objects from a collection in detail. Most recently this was the case while working on automating the provisioning of resource mailboxes for a client. I was building out the process and validation logic that we were going to use and needed to examine the ‘intermediate’ objects being produced by the process prior to their use to generate the real objects in Exchange Online.</p>
<p>Here’s the quick function that I came up with to let me output each object to Format-List, one at a time, so that I wouldn’t have to try to scroll up and down in my console to do the examination, and with optional Clear-Screen between each output.</p>
<h2 id="code">code</h2>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">function </span>Show-One
<span class="o">{</span>
<span class="o">[</span><span class="na">cmdletbinding</span><span class="o">()]</span>
<span class="k">param</span>
<span class="o">(</span>
<span class="o">[</span>parameter<span class="o">(</span>ValueFromPipeline<span class="o">)]</span>
<span class="o">[</span>psobject[]]<span class="nv">$PSObject</span>
,
<span class="o">[</span><span class="k">switch</span><span class="o">]</span><span class="nv">$ClearHost</span>
<span class="o">)</span>
<span class="k">process</span>
<span class="o">{</span>
<span class="k">foreach</span> <span class="o">(</span><span class="nv">$o</span> <span class="k">in</span> <span class="nv">$PSObject</span><span class="o">)</span>
<span class="o">{</span>
<span class="k">if</span> <span class="o">(</span><span class="nv">$true</span> -eq <span class="nv">$quit</span><span class="o">)</span>
<span class="o">{</span>
<span class="k">break</span>
<span class="o">}</span>
<span class="k">if</span> <span class="o">(</span><span class="nv">$true</span> -eq <span class="nv">$Clearhost</span><span class="o">)</span> <span class="o">{</span><span class="nb">Clear</span>-Host<span class="o">}</span>
<span class="nv">$o</span> | <span class="nb">Format-List</span> -Property <span class="k">*</span> -Force
<span class="k">if</span> <span class="o">(</span><span class="k">$(</span><span class="nb">Read-Host</span> -Prompt <span class="s2">"Show next object (any key), or quit (q)?"</span><span class="k">)</span> -eq <span class="s1">'q'</span><span class="o">)</span>
<span class="o">{</span>
<span class="nv">$quit</span> <span class="o">=</span> <span class="nv">$true</span>
<span class="o">}</span>
<span class="o">}</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<h2 id="usage-examples">usage examples</h2>
<h3 id="example-1---pipeline-input-usage">example 1 - Pipeline input usage</h3>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Get-AzureADDomain | Show-One
<span class="c1"># with -ClearHost</span>
Get-AzureADDomain | Show-One -clearhost
</code></pre></div></div>
<p>see the note below about canceling pipelines.</p>
<h3 id="example-2---parameter-input-usage">example 2 - Parameter input usage</h3>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$AADDomains</span> <span class="o">=</span> Get-AzureADDomain
Show-One -Psobject <span class="nv">$AzureADDomains</span>
</code></pre></div></div>
<h3 id="output-example">output example</h3>
<p><img src="https://www.thatexactmike.com/assets/images/Show-OneOutputExample.png" alt="no-alignment" /></p>
<h2 id="ongoing-usage">ongoing usage</h2>
<p>I added this function to my <a href="https://github.com/exactmike/profile">‘profile’ module</a> on github in the UtilityFunctions.ps1 file.</p>
<h1 id="stopping-a-pipeline">stopping a pipeline</h1>
<p>While writing this function what I really wanted to do was cancel the processing of the pipeline in the process block if the user entered ‘q’ at the read-host prompt instead of using the workaround that I implemented with the $quit variable. However, that’s not (easily) possible without implementing some complex functions. PowerShell does have this functionality in the Select-Object cmdlet via the -first parameter. But the ability to stop a pipeline is not exposed as a cmdlet or public method. Help upvote <a href="https://github.com/PowerShell/PowerShell/issues/3821">the issue on github</a>.</p>Mike Campbellfunction for quickly/easily examining the details of objects in a collectionStart-Jekyll_AzureDevOps_Blog2018-10-17T00:00:00-05:002019-01-10T00:00:00-06:00https://www.thatexactmike.com/Start-Jekyll_AzureDevops_Blog<h2 id="background">background</h2>
<p>I used the design of this blog and it’s workflow to help me learn more about devops pipelines and to specifically explore features of the recently re-branded Visual Studio Team Services (now known as Azure devops). I also dove into some (new to me) technologies for generating a static website. Here’s the stack of technology that I am currently using to design, publish, and maintain this site:</p>
<h2 id="blog-technology-stack">blog technology stack</h2>
<ul>
<li>on x # of workstations:
<ul>
<li>Windows 10 with Ubuntu app installed</li>
<li>Jekyll on Ubuntu on Windows 10 (static site generator)</li>
<li>VcXsrv on Ubuntu on Windows 10 (x-Server to support native vscode)</li>
<li>VSCode on ubuntu on Windows 10</li>
<li>Git repository for the local copy of the blog (clone of the github repo below)</li>
</ul>
</li>
<li>in the ‘cloud’
<ul>
<li>Git repository on github connected to Azure DevOps (origin for any local copies)</li>
<li>Azure Devops Build Pipeline that builds the site using Jekyll</li>
<li>Azure Devops Release Pipeline that publishes the built site to Azure Storage</li>
<li>Azure Storage static website hosting</li>
<li>Cloudflare CDN for DNS, SSL, etc.</li>
</ul>
</li>
<li>other tools
<ul>
<li><a href="https://mmistakes.github.io/minimal-mistakes/">Minimal Mistakes template</a></li>
</ul>
</li>
</ul>
<h2 id="initial-setup-process">initial setup process</h2>
<p>Getting up and running with ubuntu, ruby, jekyll and git was pretty easy (what I’ll call the baseline dev environment here). It literally took just a few minutes to get to the point of locally running a jekyll site. Being new to how jekyll templates work, the tricky part was getting my chosen template, Minimal Mistakes ‘installed’.</p>
<h3 id="getting-the-baseline-dev-environment-up-and-running">getting the baseline dev environment up and running</h3>
<h4 id="from-windows">from Windows</h4>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="nb">Enable-WindowsOptionalFeature</span> -Online -FeatureName Microsoft-Windows-Subsystem-Linux
<span class="nb">Invoke-WebRequest</span> -Uri https://aka.ms/wsl-ubuntu-1804 -OutFile Ubuntu.appx -UseBasicParsing
</code></pre></div></div>
<h4 id="from-ubuntu">from Ubuntu</h4>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="c"># update/upgrade unbuntu</span>
<span class="nb">sudo </span>apt update <span class="o">&&</span> <span class="nb">sudo </span>apt upgrade
<span class="c"># add ruby</span>
<span class="nb">sudo </span>apt-get <span class="nb">install </span>ruby ruby-dev build-essential
<span class="c"># remove unneeded</span>
<span class="nb">sudo </span>apt-get autoremove
<span class="c"># ruby environment setup</span>
<span class="nb">echo</span> <span class="s1">'# Install Ruby Gems to ~/gems'</span> <span class="o">>></span> ~/.bashrc
<span class="nb">echo</span> <span class="s1">'export GEM_HOME=$HOME/gems'</span> <span class="o">>></span> ~/.bashrc
<span class="nb">echo</span> <span class="s1">'export PATH=$HOME/gems/bin:$PATH'</span> <span class="o">>></span> ~/.bashrc
<span class="c"># make the environment settings above effective</span>
<span class="nb">source</span> ~/.bashrc
<span class="c"># configure/install gems</span>
gem update
gem <span class="nb">install </span>jekyll bundler
<span class="nb">sudo </span>apt-get <span class="nb">install </span>zlib1g-dev <span class="c">#found this was needed by jekyll or bundler</span>
bundle config path vendor/bundle
<span class="c"># create a directory to contain the new site</span>
jekyll new .#create the <span class="s1">'base'</span> jekyll site - see below <span class="k">for </span>further information
git init
git add <span class="k">*</span>
git commit <span class="nt">-m</span> <span class="s1">'initial commit'</span>
<span class="c"># set up your repo on Github and then add it as remote origin</span>
</code></pre></div></div>
<p class="notice--info">Note: To contain the site files I used a directory under my Windows user profile ($env:USERPROFILE). However, I use ubuntu vscode and git to edit the contents. I will drop new files in there via Windows but avoid editing them from the Windows side after they are in there. There are probably other ways to handle the potential conflicts but this is what I’ve settled on for now.</p>
<h3 id="getting-linux-vscode-working-on-ubuntuwindows">getting linux vscode working on ubuntu/windows</h3>
<p>I followed this <a href="https://gist.github.com/fedme/604c6b811939468fdad06e3fbba942ed">gist</a> to get an xserver running and to install/run vs code.</p>
<h3 id="getting-minimal-mistakes-installed">getting Minimal-Mistakes installed</h3>
<p>I’m admittedly a complete newb with regard to jekyll. I didn’t want to fork the minimal-mistakes repo since there was an option to install the template as a gem. However, I found that installing via gem method did not really give me a complete site. I still had to either previously generate or later copy some files and folders from the minimal-mistakes repo or from other minimal-mistakes users (<a href="https://powershell.anovelidea.org/">Thanks, Dave!</a>)</p>
<p>To install the template as a gem I did the following:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
jekyll new <span class="nb">.</span>
</code></pre></div></div>
<p>This results in the following file/directory structure:</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="nb">Get-ChildItem</span> | <span class="nb">Select </span>Name,PSIsContainer
Name PSIsContainer
---- -------------
vendor True
_posts True
.gitignore False
404.html False
about.md False
Gemfile False
Gemfile.lock False
index.md False
_config.yml False
</code></pre></div></div>
<p>From there I followed the <a href="https://mmistakes.github.io/minimal-mistakes/docs/quick-start-guide/">instructions</a> to install minimal-mistakes as a ruby gem. However, I also cloned the <a href="https://github.com/mmistakes/minimal-mistakes">minimal-mistakes repo</a> to another location and copied _config.yml from there. Eventually, I also copied additional files/folder structure over from other minimal-mistakes based sites. Most significantly I created _pages and archive and under _data I created a naigation.yml file to control the overall site navigation.</p>
<h2 id="the-release-pipeline">the release pipeline</h2>
<p>using Github integration with Azure Devops Pipelines (Build and Release) … more detail soon. However, in the meantime, <a href="https://www.forevolve.com/en/articles/2018/07/10/how-to-deploy-and-host-a-jekyll-website-in-azure-blob-storage-using-a-vsts-continuous-deployment-pipeline-part-4/">this series of posts</a> got me started.</p>
<h2 id="ongoing-workflow">ongoing workflow</h2>
<p>… more soon</p>Mike CampbellBlogging with Jekyll, Azure DevOps, Github, Azure Storage and Cloudflare.