<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
<title type="text">Rambling Cookie Monster</title>
<generator uri="https://github.com/jekyll/jekyll">Jekyll</generator>
<link rel="self" type="application/atom+xml" href="http://ramblingcookiemonster.github.io/feed.xml" />
<link rel="alternate" type="text/html" href="http://ramblingcookiemonster.github.io" />
<updated>2019-06-02T02:06:03+00:00</updated>
<id>http://ramblingcookiemonster.github.io/</id>
<author>
  <name>Warren F</name>
  <uri>http://ramblingcookiemonster.github.io/</uri>
  
</author>


<entry>
  <title type="html"><![CDATA[Winlogbeat and pipelines and painless scripts, oh my!]]></title>
  <link rel="alternate" type="text/html" href="http://ramblingcookiemonster.github.io/winlogbeat-pipeline/" />
  <id>http://ramblingcookiemonster.github.io/winlogbeat-pipeline</id>
  <updated>2019-05-18T07:00:00+00:00</updated>
  <published>2019-05-18T07:00:00+00:00</published>
  
  <author>
    <name>Warren F</name>
    <uri>http://ramblingcookiemonster.github.io</uri>
    
  </author>
  <content type="html">
    &lt;section id=&quot;table-of-contents&quot; class=&quot;toc&quot;&gt;
  &lt;header&gt;
    &lt;h3&gt;&lt;i class=&quot;fa fa-book&quot;&gt;&lt;/i&gt; Overview&lt;/h3&gt;
  &lt;/header&gt;
&lt;div id=&quot;drawer&quot;&gt;
&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#rambling&quot; id=&quot;markdown-toc-rambling&quot;&gt;Rambling&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#overview&quot; id=&quot;markdown-toc-overview&quot;&gt;Overview&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#winlogbeat&quot; id=&quot;markdown-toc-winlogbeat&quot;&gt;Winlogbeat&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#what-we-care-about&quot; id=&quot;markdown-toc-what-we-care-about&quot;&gt;What we care about&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#point-to-a-pipeline-and-index&quot; id=&quot;markdown-toc-point-to-a-pipeline-and-index&quot;&gt;Point to a pipeline and index&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#do-some-more-client-side-processing&quot; id=&quot;markdown-toc-do-some-more-client-side-processing&quot;&gt;Do some more client side processing&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#ingest-pipelines&quot; id=&quot;markdown-toc-ingest-pipelines&quot;&gt;Ingest pipelines&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#what-now&quot; id=&quot;markdown-toc-what-now&quot;&gt;What now?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

  &lt;/div&gt;
&lt;/section&gt;
&lt;!-- /#table-of-contents --&gt;

&lt;h2 id=&quot;rambling&quot;&gt;Rambling&lt;/h2&gt;

&lt;p&gt;Whew!  It’s been about 2 weeks since the 2019 PowerShell + DevOps Global Summit - the first conference I’ve played a role in organizing.  It was super fun, and it was an honor to get a chance to help with &lt;a href=&quot;http://ramblingcookiemonster.github.io/Summit-CFP/&quot;&gt;content&lt;/a&gt; for the summit… that said, it’s been a nice two weeks of recovering!&lt;/p&gt;

&lt;p&gt;A lot of folks asked what I do nowadays!  I gave my usual :headtilt: &lt;code class=&quot;highlighter-rouge&quot;&gt;computers?&lt;/code&gt; response, but the gist of it is, while I still do quite a bit with PowerShell, I have a bunch of other building blocks to design solutions with!  One of the more fun ones has been Elastic Stack.&lt;/p&gt;

&lt;p&gt;So!  Today we’ll dive into using Winlogbeat and ingest pipelines, in case it saves anyone else from wading through disparate documentation with no clear layout of an end-to-end example.&lt;/p&gt;

&lt;p&gt;Disclaimer:  I know way, way less about logging and Elastic Stack than I do about PowerShell.&lt;/p&gt;

&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;/h2&gt;

&lt;p&gt;Hopefully, you already have structured logging.  If you don’t, or if you’re paying Splunk-level-money, my condolences.&lt;/p&gt;

&lt;p&gt;Things we won’t be talking about today:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;How important structured logging is, the many critical use cases that it enables, or any of that jazz.  I assume you know it’s important&lt;/li&gt;
  &lt;li&gt;Spinning up a cluster, or even a single test node.  You can find that elsewhere&lt;/li&gt;
  &lt;li&gt;PowerShell&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So… what are we going to talk about?  We’re going to show a slightly-more-useful config than the vanilla Winlogbeat examples you see out there, including:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;how to filter log sources before sending them on to Elasticsearch&lt;/li&gt;
  &lt;li&gt;how to point logs at ingest pipelines&lt;/li&gt;
  &lt;li&gt;how to create an ingest pipeline&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s get going!&lt;/p&gt;

&lt;h2 id=&quot;winlogbeat&quot;&gt;Winlogbeat&lt;/h2&gt;

&lt;p&gt;We tend to use Elastic Beats clients - there are other options out there, including NXLog.&lt;/p&gt;

&lt;p&gt;We’ll use Winlogbeat in this example.  There are a few pieces we’ll dive into:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;what logs we care about&lt;/li&gt;
  &lt;li&gt;whether any of the logs need to be processed in a pipeline&lt;/li&gt;
  &lt;li&gt;whether to process anything on the client&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;what-we-care-about&quot;&gt;What we care about&lt;/h3&gt;

&lt;p&gt;So!  First, we’ll tell Winlogbeat what we care about, and tack on information about which pipeline to ship logs to.&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;s&quot;&gt;winlogbeat.event_logs&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Security&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;ignore_older&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;168h&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;fields&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;rc_ingest_pipeline&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;windows'&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;rc_index_name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;windows'&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;fields_under_root&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Microsoft-Windows-DNS-Client/Operational'&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;ignore_older&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;168h&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;processors&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;drop_event.when.not&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;and&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;equals.event_id&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;3008&lt;/span&gt;
          &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;and&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;not.equals.event_data.QueryOptions&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;140737488355328'&lt;/span&gt;
            &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;not.equals.event_data.QueryResults&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;System&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;ignore_older&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;168h&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;processors&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;drop_event.when.not&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;or&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;equals.source_name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Microsoft-Windows-Eventlog'&lt;/span&gt;
          &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;and&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;equals.source_name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Microsoft-Windows-GroupPolicy'&lt;/span&gt;
            &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;equals.level&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Error&lt;/span&gt;
            &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;or&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
              &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;equals.event_id&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;1085&lt;/span&gt;
              &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;equals.event_id&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;1125&lt;/span&gt;
              &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;equals.event_id&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;1127&lt;/span&gt;
              &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;equals.event_id&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;1129&lt;/span&gt;
          &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;and&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;equals.source_name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Microsoft-Windows-Eventlog'&lt;/span&gt;
            &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;equals.level&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Information&lt;/span&gt;
            &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;equals.event_id&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;104&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This looks ugly but it gives you an idea of the flexibility you can get, even if you stick to yaml.&lt;/p&gt;

&lt;p&gt;In this example:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;I want some operational DNS client log, but only if they are event id 3008, and aren’t associated with empty or local machine name resolution&lt;/li&gt;
  &lt;li&gt;I want everything from the security log&lt;/li&gt;
  &lt;li&gt;I want three varieties (&lt;code class=&quot;highlighter-rouge&quot;&gt;or&lt;/code&gt;) of events from the System log&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can nest &lt;code class=&quot;highlighter-rouge&quot;&gt;and&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;or&lt;/code&gt; statements, which gives you some nice flexibility.  I err on the side of collecting too much, but these filters at least get me started.&lt;/p&gt;

&lt;h3 id=&quot;point-to-a-pipeline-and-index&quot;&gt;Point to a pipeline and index&lt;/h3&gt;

&lt;p&gt;Now!  You might have noticed something odd in the &lt;code class=&quot;highlighter-rouge&quot;&gt;Security&lt;/code&gt; bit:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Security&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;ignore_older&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;168h&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;fields&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;rc_ingest_pipeline&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;windows'&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;rc_index_name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;windows'&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;fields_under_root&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is saying that for &lt;em&gt;every&lt;/em&gt; event in the Security event log, I want to tack on an &lt;code class=&quot;highlighter-rouge&quot;&gt;rc_ingest_pipeline&lt;/code&gt; &lt;a href=&quot;https://www.elastic.co/guide/en/beats/winlogbeat/current/configuration-general-options.html#libbeat-configuration-fields&quot;&gt;field&lt;/a&gt;, with the value &lt;code class=&quot;highlighter-rouge&quot;&gt;windows&lt;/code&gt; (&lt;a href=&quot;https://www.elastic.co/guide/en/beats/winlogbeat/current/configuration-general-options.html#_literal_fields_under_root_literal&quot;&gt;fields_under_root&lt;/a&gt; just means I don’t want a nested property).  This is how I tell Winlogbeat which ingest pipeline I want to use.  In this case, I just have a generic catch-all &lt;code class=&quot;highlighter-rouge&quot;&gt;windows&lt;/code&gt; pipeline, but you’ll likely have many others.&lt;/p&gt;

&lt;p&gt;Jumping ahead, in the output section, you’ll see that we use the field we just created to point to a pipeline:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;s&quot;&gt;output.elasticsearch&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;hosts&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;https://REDACTED01:9200&quot;&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;https://REDACTED02:9200&quot;&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;username&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;REDACTED&quot;&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;password&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;REDACTED&quot;&lt;/span&gt;
  &lt;span class=&quot;s&quot;&gt;ssl.certificate_authorities&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;C:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;beats&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;ca.crt&quot;&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;pipeline&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;%{[rc_ingest_pipeline]}&quot;&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;%{[rc_index_name]}&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;By using &lt;code class=&quot;highlighter-rouge&quot;&gt;&quot;%{[rc_ingest_pipeline]}&quot;&lt;/code&gt;, we’re evaluating to whatever the value of the event’s rc_ingest_pipeline field is - note that we could have defined different values for different log sources.  There are several other ways to route events, including &lt;a href=&quot;https://www.elastic.co/guide/en/beats/winlogbeat/current/elasticsearch-output.html#pipelines-option-es&quot;&gt;pipelines&lt;/a&gt; with &lt;a href=&quot;https://www.elastic.co/guide/en/beats/winlogbeat/master/defining-processors.html#conditions&quot;&gt;conditions&lt;/a&gt;, or just a hard coding &lt;code class=&quot;highlighter-rouge&quot;&gt;output.elasticsearch.pipeline&lt;/code&gt; rather than using the variable expansion we used.&lt;/p&gt;

&lt;h3 id=&quot;do-some-more-client-side-processing&quot;&gt;Do some more client side processing&lt;/h3&gt;

&lt;p&gt;We’re almost done with the config!  There’s one last spot we can have some fun:  &lt;a href=&quot;https://www.elastic.co/guide/en/beats/winlogbeat/current/defining-processors.html&quot;&gt;Processors&lt;/a&gt;!  In this example, I just remove the &lt;code class=&quot;highlighter-rouge&quot;&gt;message&lt;/code&gt; field from some of our most common and verbose events (i.e. those that chew up storage without much benefit):&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;processors&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;drop_fields&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;when&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;or&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;equals.event_id&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;4624&lt;/span&gt;
          &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;equals.event_id&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;4634&lt;/span&gt;
          &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;equals.event_id&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;4625&lt;/span&gt;
          &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;equals.event_id&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;4648&lt;/span&gt;
          &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;equals.event_id&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;4768&lt;/span&gt;
          &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;equals.event_id&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;4658&lt;/span&gt;
          &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;equals.event_id&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;4776&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;fields&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;message&quot;&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That’s about it!  Now, lets look at that &lt;code class=&quot;highlighter-rouge&quot;&gt;windows&lt;/code&gt; ingest pipeline we’re routing some events to.&lt;/p&gt;

&lt;h2 id=&quot;ingest-pipelines&quot;&gt;Ingest pipelines&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://www.elastic.co/guide/en/elasticsearch/reference/master/ingest.html&quot;&gt;Ingest&lt;/a&gt; nodes let you process things.  They’re generally more efficient, but &lt;a href=&quot;https://discuss.elastic.co/t/is-there-any-observability-into-ingest-pipelines/176130&quot;&gt;less observable&lt;/a&gt; than Logstash pipelines.  Do consider the implications; personally, I would tend to lean towards Logstash in most scenarios.  Oh well!&lt;/p&gt;

&lt;p&gt;First, we need to define the pipeline.  You can use a number of &lt;a href=&quot;https://www.elastic.co/guide/en/elasticsearch/reference/master/ingest-processors.html&quot;&gt;processors&lt;/a&gt;, including &lt;a href=&quot;https://www.elastic.co/guide/en/elasticsearch/reference/master/grok-processor.html&quot;&gt;grok&lt;/a&gt; and &lt;a href=&quot;https://www.elastic.co/guide/en/elasticsearch/reference/master/geoip-processor.html&quot;&gt;geopip&lt;/a&gt;, but for these events, I just want to run a &lt;a href=&quot;https://www.elastic.co/guide/en/elasticsearch/reference/master/script-processor.html&quot;&gt;script&lt;/a&gt;, and I want to know if any errors come up (tip: they will, particularly on your first iterations of writing a script)&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
{
    &quot;description&quot;: &quot;Process Windows event log&quot;,
    &quot;processors&quot;: [
        {
            &quot;script&quot;: {
                &quot;id&quot;: &quot;windows&quot;
            }
        }
    ],
    &quot;on_failure&quot;: [
        {
            &quot;set&quot;: {
                &quot;field&quot;: &quot;pipeline_error&quot;,
                &quot;value&quot;: &quot;true&quot;
            }
        },
        {
            &quot;set&quot; : {
                &quot;field&quot; : &quot;pipeline_error_message&quot;,
                &quot;value&quot; : &quot;{{ _ingest.on_failure_message }}&quot;
              }
        },
        {
            &quot;set&quot; : {
                &quot;field&quot; : &quot;pipeline_error_type&quot;,
                &quot;value&quot; : &quot;{{ _ingest.on_failure_processor_type }}&quot;
              }
        }
    ]
}

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

&lt;p&gt;All I have is a pointer that says &lt;code class=&quot;highlighter-rouge&quot;&gt;run this script called 'windows'!&lt;/code&gt;, and some &lt;a href=&quot;https://www.elastic.co/guide/en/elasticsearch/reference/master/handling-failure-in-pipelines.html&quot;&gt;error handling&lt;/a&gt; to give me info on whether an error occurred, the error message, and the error type.&lt;/p&gt;

&lt;p&gt;This is important.  You want a quick way to see if any events aren’t being processed correctly - by adding these fields, you can search for and alert on any processing errors.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/ingest/ingest-error.png&quot; alt=&quot;Grok error&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Right.  Maybe don’t alert on errors when you fail to grok a field that’s based on user input!&lt;/p&gt;

&lt;p&gt;Next, we need to actually define the script.  We’ll be using &lt;a href=&quot;https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-scripting-painless.html&quot;&gt;painless&lt;/a&gt;.  This is a misnomer.&lt;/p&gt;

&lt;p&gt;I like a little context.  I don’t want the whole &lt;code class=&quot;highlighter-rouge&quot;&gt;message&lt;/code&gt; field, I just want a brief &lt;em&gt;what is this event for?&lt;/em&gt;.  I also don’t memorize enums, so I translate things like logon type.&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;log_name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Security&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;event_data&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;LogonType&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;logonmap&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;sc&quot;&gt;'0'&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;sc&quot;&gt;'2'&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Interactive&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;sc&quot;&gt;'3'&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Network&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;sc&quot;&gt;'4'&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Batch&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;sc&quot;&gt;'5'&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Services&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;sc&quot;&gt;'7'&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Unlock&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;sc&quot;&gt;'8'&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NetworkClearText&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;sc&quot;&gt;'9'&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NewCredentials&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;err&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RemoteInteractive&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;err&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;11&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Cached&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;'&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;];&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;event_data&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;LogonType&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newvalue&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;logonmap&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;];&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;newvalue&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;rc_logon_type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newvalue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;err&quot;&gt;#&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Defining&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;without&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;initializing&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;it&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;map&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;broke&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;things&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;log_name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Security&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Map&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;eventmap&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HashMap&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;

    &lt;span class=&quot;err&quot;&gt;#&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;subset&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ids&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;the&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sake&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;brevity&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;....&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;eventmap&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4720&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;A&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;account&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;was&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;created&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;eventmap&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4722&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;A&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;account&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;was&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;enabled&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;eventmap&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4723&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;An&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;attempt&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;was&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;made&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;change&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;an&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;account&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;password&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;eventmap&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4724&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;An&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;attempt&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;was&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;made&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;reset&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;an&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;account&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;password&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;eventmap&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4725&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;A&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;account&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;was&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;disabled&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;eventmap&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4726&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;A&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;account&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;was&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;deleted&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;eventmap&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4727&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;A&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;security&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;enabled&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;global&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;group&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;was&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;created&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;eventmap&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4728&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;A&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;member&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;was&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;added&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;security&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;enabled&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;global&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;eventmap&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4729&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;A&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;member&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;was&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;removed&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;security&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;enabled&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;global&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;eventmap&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4730&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;A&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;security&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;enabled&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;global&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;group&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;was&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;deleted&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;eventmap&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4731&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;A&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;security&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;enabled&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;local&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;group&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;was&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;created&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;valueOf&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;event_id&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newvalue&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;eventmap&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;];&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;newvalue&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;rc_description&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newvalue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That’s it!  In the script, I make some assumptions and create fields to indicate the description of the event, and to have a friendly name for the logon type.  I like context and convenience - you might consider adding much more than this : )&lt;/p&gt;

&lt;p&gt;Do note that I’m doing the same thing - assigning a value based on a hashmap - in two ways.  I ran into errors when I tried the simplified syntax on a hashmap with ~420 keys, and switching syntax fixed things.&lt;/p&gt;

&lt;h2 id=&quot;what-now&quot;&gt;What now?&lt;/h2&gt;

&lt;p&gt;There’s a lot of flexibility here!&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;The same Winlogbeat config syntax for pipelines can be used in Filebeat&lt;/li&gt;
  &lt;li&gt;You might explore parsing the windows firewall logs, DHCP logs, and other file logs with ingest pipelines, grok, and painless scripts&lt;/li&gt;
  &lt;li&gt;If you care about the location of an IP, &lt;a href=&quot;https://www.elastic.co/guide/en/elasticsearch/reference/7.0/geoip-processor.html&quot;&gt;geoip&lt;/a&gt; is built in now, and is pretty handy (The database is built into Elastic’s container - yay! but you’ll need to mess with templates to indicate the data type - boo!)&lt;/li&gt;
  &lt;li&gt;Honestly?  You might want to use Logstash.  There are &lt;a href=&quot;https://www.syspanda.com/index.php/2018/05/03/monitoring-active-directory-elk/&quot;&gt;more examples&lt;/a&gt; in the wild, and you can get data on the performance of each component in a pipeline.  The efficiency of ingest pipelines, &lt;em&gt;assuming you don’t need it&lt;/em&gt;, is likely less valuable than the benefits you get from using Logstash&lt;/li&gt;
  &lt;li&gt;Palantir is bad, but &lt;a href=&quot;https://github.com/palantir/windows-event-forwarding&quot;&gt;this repo&lt;/a&gt; has some very useful data about what you might care about in the world of Windows event logs.  I translated some of this to a Winlogbeat config.  At some point, it would be awesome if someone were ambitious enough to write an WEF XML to Winlogbeat yaml translation tool : )&lt;/li&gt;
  &lt;li&gt;You probably noticed I point Security events to a particular index name.  This points us to an &lt;a href=&quot;https://www.elastic.co/guide/en/elasticsearch/reference/current/getting-started-index-lifecycle-management.html&quot;&gt;index lifecycle management&lt;/a&gt; policy.  These are useful!&lt;/li&gt;
  &lt;li&gt;There’s a ton of interesting tools and blogs out there.  Many can be found in security-oriented places.  &lt;a href=&quot;https://cyberwardog.blogspot.com/&quot;&gt;Roberto Rodriguez&lt;/a&gt; has some awesome material, including tools like &lt;a href=&quot;https://github.com/Cyb3rWard0g/HELK&quot;&gt;HELK&lt;/a&gt;.  &lt;a href=&quot;https://www.syspanda.com/index.php/category/elasticsearch/&quot;&gt;Pablo Delgado&lt;/a&gt; has some handy materials as well&lt;/li&gt;
  &lt;li&gt;Don’t restrict yourself to built-in log sources!  Be sure to consider integrating other tools like Sysmon into the flow.  You might even generate your own event log entries from your various scripts and software, and pull it into Elasticsearch, where you can alert on it, report on it, etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s about it!  PowerShell?  Sure.  You’re not defining everything in the Kibana dev tools, right?  You might have a repo that has ingest pipelines, templates, index patterns, ilm policies, scripts, watchers, and other Elasticsearch or Kibana config that can be defined in code, that you test / lint before pushing to Elasticsearch or Kibana in your build.  A glue language like PowerShell is great for this!&lt;/p&gt;

    &lt;p&gt;&lt;a href=&quot;http://ramblingcookiemonster.github.io/winlogbeat-pipeline/&quot;&gt;Winlogbeat and pipelines and painless scripts, oh my!&lt;/a&gt; was originally published by Warren F at &lt;a href=&quot;http://ramblingcookiemonster.github.io&quot;&gt;Rambling Cookie Monster&lt;/a&gt; on May 18, 2019.&lt;/p&gt;
  </content>
</entry>


<entry>
  <title type="html"><![CDATA[So You Want to Go to the Summit]]></title>
  <link rel="alternate" type="text/html" href="http://ramblingcookiemonster.github.io/Go-To-Summit/" />
  <id>http://ramblingcookiemonster.github.io/Go-To-Summit</id>
  <updated>2018-11-01T07:00:00+00:00</updated>
  <published>2018-11-01T07:00:00+00:00</published>
  
  <author>
    <name>Warren F</name>
    <uri>http://ramblingcookiemonster.github.io</uri>
    
  </author>
  <content type="html">
    &lt;section id=&quot;table-of-contents&quot; class=&quot;toc&quot;&gt;
  &lt;header&gt;
    &lt;h3&gt;&lt;i class=&quot;fa fa-book&quot;&gt;&lt;/i&gt; Overview&lt;/h3&gt;
  &lt;/header&gt;
&lt;div id=&quot;drawer&quot;&gt;
&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#rambling&quot; id=&quot;markdown-toc-rambling&quot;&gt;Rambling&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#so-you-want-to-go-to-the-summit&quot; id=&quot;markdown-toc-so-you-want-to-go-to-the-summit&quot;&gt;So you want to go to the summit&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#the-people&quot; id=&quot;markdown-toc-the-people&quot;&gt;The People&lt;/a&gt;        &lt;ul&gt;
          &lt;li&gt;&lt;a href=&quot;#tips&quot; id=&quot;markdown-toc-tips&quot;&gt;Tips&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#the-content&quot; id=&quot;markdown-toc-the-content&quot;&gt;The Content&lt;/a&gt;        &lt;ul&gt;
          &lt;li&gt;&lt;a href=&quot;#tips-1&quot; id=&quot;markdown-toc-tips-1&quot;&gt;Tips&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

  &lt;/div&gt;
&lt;/section&gt;
&lt;!-- /#table-of-contents --&gt;

&lt;h2 id=&quot;rambling&quot;&gt;Rambling&lt;/h2&gt;

&lt;p&gt;I guess I’ve been doing this a while!  I first harassed my manager about the PowerShell Summit back in 2013.&lt;/p&gt;

&lt;p&gt;Back then, I would look at the sessions, look at my team’s gaps, projects, and goals, and point out the sessions that could help.  Nothing more.&lt;/p&gt;

&lt;p&gt;Nowadays?  The breakout sessions themselves are awesome, but aren’t even in the top three reasons I give my boss.&lt;/p&gt;

&lt;h2 id=&quot;so-you-want-to-go-to-the-summit&quot;&gt;So you want to go to the summit&lt;/h2&gt;

&lt;p&gt;I’m just going to list out some benefits to going to &lt;a href=&quot;https://powershell.org/summit/&quot;&gt;the summit&lt;/a&gt;.  Depending on where you are in your career, what you already know, etc. some of these may or may not be helpful.&lt;/p&gt;

&lt;h3 id=&quot;the-people&quot;&gt;The People&lt;/h3&gt;

&lt;p&gt;The people you see are hands down the biggest benefit of the summit for most folks.&lt;/p&gt;

&lt;p&gt;Who will you meet?&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Folks on the PowerShell team, and some alumni&lt;/li&gt;
  &lt;li&gt;Various leaders in the community - Microsoft MVPs, how-are-they-not-MVPs, speakers, and more&lt;/li&gt;
  &lt;li&gt;Folks whose work will send them to a PowerShell + DevOps summit, or who fund it themselves&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now… Why does this matter?  How can this be used to justify going to the summit?  (s/they/folks you meet)&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;They might have a great opportunity for you at some point in your career&lt;/li&gt;
  &lt;li&gt;You might have an opportunity they’re an awesome fit for at some point&lt;/li&gt;
  &lt;li&gt;They might be an expert in some topic you need guidance on at some point&lt;/li&gt;
  &lt;li&gt;If they’re involved organizing conferences or user groups, you might get a chance to speak&lt;/li&gt;
  &lt;li&gt;They might have some ideas or conversations that give you an idea on how to solve something at work&lt;/li&gt;
  &lt;li&gt;They might be working on or know about a project / tool that would be perfect for your org&lt;/li&gt;
  &lt;li&gt;Folks can share more in person than in a blog / public.  You’ll find folks from Microsoft, Amazon, and many other companies doing awesome and interesting things, who might not otherwise blog or publicize certain things&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Meeting new folks doesn’t come natural to everyone.  Certainly not me.  What can help?  How can you get the most out of this?&lt;/p&gt;

&lt;h4 id=&quot;tips&quot;&gt;Tips&lt;/h4&gt;

&lt;p&gt;Get to know folks!&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Join the community&lt;/strong&gt;.  Ideally before the summit.  Twitter, Slack (Summit Slack and &lt;a href=&quot;https://bit.ly/psslack&quot;&gt;PowerShell Slack&lt;/a&gt; at least), &lt;a href=&quot;https://reddit.com/r/powershell&quot;&gt;Reddit&lt;/a&gt;, &lt;a href=&quot;https://powershell.org/&quot;&gt;PowerShell.org&lt;/a&gt;, etc.  Ask questions.  Help others.  Chances are you’ll meet folks from these communities in person, and this is a great way to keep in touch with folks after the summit.  You never know when they might have a job opportunity pop up!&lt;/li&gt;
  &lt;li&gt;If you can, &lt;strong&gt;put your phone away&lt;/strong&gt; at breakfast, lunch, and breaks.  Say hi to new folks or folks you’ve already met&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Go out to dinner and evening events with the folks you meet&lt;/strong&gt;.  Check Slack (&lt;code class=&quot;highlighter-rouge&quot;&gt;where are folks headed tonight?&lt;/code&gt; will surely net you one or more choices!)&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Attending with co-workers?  Ditch them.&lt;/strong&gt;  Or at the very least, be sure you’re not shying away from meeting other folks simply because it’s easier to stick with a co-worker&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Don’t be afraid to start the conversation&lt;/strong&gt;.  There are plenty of simple questions to start with!  &lt;code class=&quot;highlighter-rouge&quot;&gt;So where are you from?&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;Is this your first summit?&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;So what do you do?&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;Any fun projects you're working on?&lt;/code&gt;, etc.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Introduce yourself to the speakers, organizers, Microsoft folks, etc.&lt;/strong&gt;  They’re all just people.  Don’t be afraid to talk to them!&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Work can wait&lt;/strong&gt;.  You only get this opportunity once a year, don’t eat the most valuable time at the summit working, if you can help it&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Introverted?  Drain your batteries&lt;/strong&gt;.  Seriously.  You can recharge after the summit.  Eat lunch with folks.  Walk up to new folks and start a conversation.  Go out for food and other fun in the evening&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Keep coming back&lt;/strong&gt;.  It gets easier every year.  As you get to know folks in the online communities, and come back to the summit, it becomes increasingly easier to find folks you know.  But… do meet new folks!&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Never take the red eye&lt;/strong&gt;.  Thursday night gives you one last chance to catch up with folks.  Some folks do a huge pub crawl.  Others (me!) prefer a quiet night getting food and drinks with other summiteers&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;the-content&quot;&gt;The Content&lt;/h3&gt;

&lt;p&gt;So!  On top of people and hallway track that they create, you have actual summit content.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;General sessions (everyone in the same room)&lt;/li&gt;
  &lt;li&gt;Breakout sessions (45 minute or 90 minute bits)&lt;/li&gt;
  &lt;li&gt;Side sessions that can range from lecture-ish bits, to group discussions, to hackathons&lt;/li&gt;
  &lt;li&gt;Lightning demos - 10 minute or less talks that move fast and introduce you to interesting things you could dive into&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The schedule won’t be completely filled out before you ask for approval, but there should be enough to help.&lt;/p&gt;

&lt;p&gt;There are a few ways you can use this to encourage your boss to send you:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Line up session topics with projects or goals at work&lt;/li&gt;
  &lt;li&gt;Line up speaker expertise with projects or goals at work - you can talk to these folks after all&lt;/li&gt;
  &lt;li&gt;If this is like the previous years, you’ll get a preview of what the PowerShell team is working on&lt;/li&gt;
  &lt;li&gt;You’ll have the chance to talk with various folks on the PowerShell team&lt;/li&gt;
  &lt;li&gt;Even if a good bit of the content is posted online, being there means you can follow up with the speakers&lt;/li&gt;
  &lt;li&gt;There’s a high signal-to-noise ratio.  This isn’t some sales-y conference with vendors and parties every night.  This is a group of your peers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In case it helps, here are a few tips on content…&lt;/p&gt;

&lt;h4 id=&quot;tips-1&quot;&gt;Tips&lt;/h4&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Take notes&lt;/strong&gt;!  What did you learn?  Who did you meet?  What were they working on?  What ideas / tools came up?  etc.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;The breakout and most general sessions are recorded&lt;/strong&gt;.  Unless you really want the opportunity to see the content and follow up with the speaker, you can catch these after the summit - &lt;strong&gt;don’t feel bad skipping them!&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;The hallway track and side sessions are not recorded&lt;/strong&gt;.  My most valuable takeaways are from people / side sessions &lt;em&gt;every year&lt;/em&gt;.  This is the stuff you can’t get from videos posted online after the conference.  &lt;strong&gt;Don’t miss these&lt;/strong&gt;!&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Plan ahead&lt;/strong&gt;!  Are there any sessions you can’t miss?  Any where you know you’ll want to hit the hallway/side sessions?  Reduce the last minute scrambles - at least have an idea of where you’ll go, and be sure to check for updated side session topics&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Look for related events&lt;/strong&gt;!  DevOps Days Seattle is a week ahead.  There &lt;em&gt;might&lt;/em&gt; be a WinOps-y nano conference the day after.  Poke around and ask folks before you book your tickets and hotel!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s pretty much it!  There’s even more you could add if you start sharing - doing lightning demos, speaking, leading side sessions, organizing, etc. - but the bits above should apply to pretty much everyone interested in the summit.&lt;/p&gt;

&lt;p&gt;Oh!  &lt;a href=&quot;https://www.eventbrite.com/e/powershell-devops-global-summit-2019-registration-45182308501&quot;&gt;Registration opens today&lt;/a&gt;.  Hope to see you there!&lt;/p&gt;

    &lt;p&gt;&lt;a href=&quot;http://ramblingcookiemonster.github.io/Go-To-Summit/&quot;&gt;So You Want to Go to the Summit&lt;/a&gt; was originally published by Warren F at &lt;a href=&quot;http://ramblingcookiemonster.github.io&quot;&gt;Rambling Cookie Monster&lt;/a&gt; on November 01, 2018.&lt;/p&gt;
  </content>
</entry>


<entry>
  <title type="html"><![CDATA[The 2019 PowerShell and DevOps Global Summit CFP]]></title>
  <link rel="alternate" type="text/html" href="http://ramblingcookiemonster.github.io/Summit-CFP/" />
  <id>http://ramblingcookiemonster.github.io/Summit-CFP</id>
  <updated>2018-10-16T07:00:00+00:00</updated>
  <published>2018-10-16T07:00:00+00:00</published>
  
  <author>
    <name>Warren F</name>
    <uri>http://ramblingcookiemonster.github.io</uri>
    
  </author>
  <content type="html">
    &lt;section id=&quot;table-of-contents&quot; class=&quot;toc&quot;&gt;
  &lt;header&gt;
    &lt;h3&gt;&lt;i class=&quot;fa fa-book&quot;&gt;&lt;/i&gt; Overview&lt;/h3&gt;
  &lt;/header&gt;
&lt;div id=&quot;drawer&quot;&gt;
&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#rambling&quot; id=&quot;markdown-toc-rambling&quot;&gt;Rambling&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#deciding&quot; id=&quot;markdown-toc-deciding&quot;&gt;Deciding&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#the-basics&quot; id=&quot;markdown-toc-the-basics&quot;&gt;The Basics&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#the-good-things&quot; id=&quot;markdown-toc-the-good-things&quot;&gt;The Good Things&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#things-that-didnt-help&quot; id=&quot;markdown-toc-things-that-didnt-help&quot;&gt;Things that didn’t help&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#logistics-and-balance&quot; id=&quot;markdown-toc-logistics-and-balance&quot;&gt;Logistics and balance&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#so-you-want-to-speak-at-the-summit&quot; id=&quot;markdown-toc-so-you-want-to-speak-at-the-summit&quot;&gt;So you want to speak at the summit&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#some-cfp-stats&quot; id=&quot;markdown-toc-some-cfp-stats&quot;&gt;Some CFP Stats&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#how-did-you-work-on-the-cfp&quot; id=&quot;markdown-toc-how-did-you-work-on-the-cfp&quot;&gt;How did you work on the CFP?&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#things-that-worked&quot; id=&quot;markdown-toc-things-that-worked&quot;&gt;Things that worked&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#things-that-didnt-work&quot; id=&quot;markdown-toc-things-that-didnt-work&quot;&gt;Things that didn’t work&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

  &lt;/div&gt;
&lt;/section&gt;
&lt;!-- /#table-of-contents --&gt;

&lt;h2 id=&quot;rambling&quot;&gt;Rambling&lt;/h2&gt;

&lt;p&gt;Whew!  It’s been a fun and busy few weeks.  I’ve been working with &lt;a href=&quot;https://twitter.com/thedevopsdiva&quot;&gt;Missy&lt;/a&gt; on reviewing proposals for the &lt;a href=&quot;https://powershell.org/summit/&quot;&gt;2019 PowerShell + DevOps Global Summit&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I was excited and honored when I was asked to help with the summit.  I’ve been coming to this summit since I started working on PowerShell, and I owe most of any success I’ve had to the various attendees and speakers who have helped me along the years.&lt;/p&gt;

&lt;p&gt;Working on the CFP has been awesome - you get to preview a ton of interesting ideas, and you get to help pick the content of the summit, which sets the direction of topics and ideas for a decent sized audience…  It’s a huge honor, and I would do it again, but I can assure you, turning down ~150 proposals, and reviewing 20 release pipeline bits can wear you out - I’m happy to be done for the year!&lt;/p&gt;

&lt;p&gt;Now that &lt;a href=&quot;https://bit.ly/summit2019agenda&quot;&gt;the agenda&lt;/a&gt; is out and speakers have been notified, I figure it’s a good time to talk about the CFP!&lt;/p&gt;

&lt;h2 id=&quot;deciding&quot;&gt;Deciding&lt;/h2&gt;

&lt;p&gt;Picking topics was tough.  Cutting topics was tougher.  Let’s look at some of the factors!&lt;/p&gt;

&lt;h3 id=&quot;the-basics&quot;&gt;The Basics&lt;/h3&gt;

&lt;p&gt;Early in the process Missy pointed to &lt;a href=&quot;https://tisiphone.net/2018/07/16/i-reviewed-600-call-for-paper-submissions-and-youll-probably-guess-what-happened-next/&quot;&gt;a solid bit on reviewing proposals for a CFP&lt;/a&gt;.  This helped serve as a gate of sorts.&lt;/p&gt;

&lt;p&gt;A few of these criteria lead to some hard _no_s.  We weren’t super strict, but some things scare a reviewer off.  If you put no time into a proposal, don’t review it, and have no one else review it, leading to a bunch of spelling and grammar errors among other issues, we might extrapolate and think… what sort of presentation and material will this author put together?&lt;/p&gt;

&lt;p&gt;Other criteria simply made it harder for us to say yes.  We had a bunch of &lt;em&gt;release pipelines&lt;/em&gt;, &lt;em&gt;Azure&lt;/em&gt;, and &lt;em&gt;Pester&lt;/em&gt; proposals.  This means you have a lot more competition - not only is your proposal up against everything else, it can be edged out by proposals on similar topics.  It also means your proposal might blend in with the others, and stand out less than an original topic.&lt;/p&gt;

&lt;p&gt;Combined with other factors, the &lt;em&gt;why is this important&lt;/em&gt; bit stood out.  Proposals are basically marketing.  We read through each proposal &lt;em&gt;multiple times&lt;/em&gt;.  If you didn’t capture our curiosity or interest, it made it much easier for your proposal to land in the &lt;em&gt;maybe&lt;/em&gt; pile, which puts it at a little disadvantage.  If it was on a popular topic &lt;em&gt;and&lt;/em&gt; it didn’t intrigue us?  It might have ended up in the &lt;em&gt;no&lt;/em&gt; pile.&lt;/p&gt;

&lt;p&gt;There was more, but those are three criteria that stood out to help as an initial filter.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Did you spend any time creating and reviewing your proposal?&lt;/li&gt;
  &lt;li&gt;Was your topic very popular?&lt;/li&gt;
  &lt;li&gt;Did you capture our interest?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So… We’ve talked about some things that lead to a &lt;em&gt;no&lt;/em&gt; or a &lt;em&gt;maybe&lt;/em&gt;.  What made us say yes?&lt;/p&gt;

&lt;h3 id=&quot;the-good-things&quot;&gt;The Good Things&lt;/h3&gt;

&lt;p&gt;Some factors helped proposals get to the &lt;em&gt;accept&lt;/em&gt; pile, for better or worse.  Keep in mind these are simply factors that could help to some degree, not hard-and-fast rules.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Was it unique?&lt;/strong&gt;  This makes it much easier to say yes.  Particularly for a popular topic.  Module or infrastructure release pipelines?  Tons of submissions.  SQL release pipelines?  &lt;a href=&quot;https://app.socio.events/MjQ4Nw/agenda/14445/session/61465&quot;&gt;One&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Was it widely applicable?&lt;/strong&gt;  If your talk can extrapolate across different products / stacks, it had a better chance.  For example, we might pick a bit on &lt;a href=&quot;https://app.socio.events/MjQ4Nw/agenda/14445/session/61482&quot;&gt;psake&lt;/a&gt;, over a similar bit specific to Azure DevOps&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Do you have any community presence?&lt;/strong&gt;.  This might be a bad one.  There are plenty of situations that lead to someone having no chance to contribute at work and at home.  That said, your work in a community can help us pick some ‘known quantity’ speakers - whether that means you’re an awesome speaker that we’ve seen, that you’re an expert in a particular topic, or that you’re quite creative and tend to do awesome things.  This could even mean just networking with folks at the summit&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Was your topic interesting?&lt;/strong&gt;  On top of being unique, having an interesting topic can help.  This really depends on the reviewer, and what they think the audience, or subsets thereof, might find interesting&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Are you the author?&lt;/strong&gt;  This is a pretty specific one, but worth pointing out.  If you’re the author or maintainer of a tool or module, chances are if there are more than one proposal on it, you’ll be speaking&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Did you submit late?&lt;/strong&gt;  We’re human.  We need to review and follow-up throughout this CFP process.  We did our best to avoid any bias based on time, but to a small degree, I think the farther along the CFP, the more compelling a proposal needed to be to land in the &lt;em&gt;accept&lt;/em&gt; pile&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Did you get lucky?&lt;/strong&gt;  Again, we’re human.  Maybe Missy or I were in a good or bad mood when we saw / re-reviewed your bit.  Maybe you ended up in the &lt;em&gt;maybe&lt;/em&gt; pile and we didn’t give your proposal as much consideration on re-review as we should have.  There are so many little things that could have added up to what I call… Luck&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Okay!  Let’s move on to some more potential road blocks.&lt;/p&gt;

&lt;h3 id=&quot;things-that-didnt-help&quot;&gt;Things that didn’t help&lt;/h3&gt;

&lt;p&gt;Some of these will be the inverse of helpful factors, or repeats from the basics…  In general, these didn’t help:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Hot topics&lt;/strong&gt;.  Proposing a popular topic?  Yours needs to stand out that much more&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Constrained topics&lt;/strong&gt;.  Azure is a good example.  Not everyone uses Azure.  If we got 20 world-beating Azure proposals from the brightest leaders in the community, we’re still only going to accept a handful or less&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Lack of context or clarity&lt;/strong&gt;.  This came up in a few ways.  If you’re talking about PowerShell with &lt;em&gt;technology xyz&lt;/em&gt;, tell us (and the audience!) what &lt;em&gt;technology xyz&lt;/em&gt; is!  If you’re giving us a recipe for solving something, give us a light outline of sorts - it can help us if we’re not sure you’ll go off in an odd direction - particularly important for new folks&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;A single proposal&lt;/strong&gt;.  I know.  It takes time to write these.  But here’s the thing: What if we happen to get a blockbuster proposal that covers what you cover?  What if you simply get unlucky?  Having two or three proposals gives you a better chance of speaking&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Repeat topics&lt;/strong&gt;.  We don’t mandate original content.  We completely understand that coming up with new talks is &lt;em&gt;hard&lt;/em&gt;, and that iterating on topics works.  That said - if you’ve given this talk at a recorded conference or even at this summit another year, you &lt;em&gt;need&lt;/em&gt; to tell us what will be different, or there’s a very high likelihood it’s going in the &lt;em&gt;no&lt;/em&gt; pile, even if it was originally a hit&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Poor fit&lt;/strong&gt;.  Did you submit a beginner level bit?  Something that isn’t PowerShell or DevOps related?  It probably won’t get picked.  Even if we let some beginner level material through, chances are it’s going to have some serious competition, and we likely won’t let anything through that would be covered in the OnRamp material&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Whew!  Okay, so we’ve talked the basics, and factors that made it more or less likely for us to select your topic.  Let’s cover a few other factors that you might not think about.&lt;/p&gt;

&lt;h3 id=&quot;logistics-and-balance&quot;&gt;Logistics and balance&lt;/h3&gt;

&lt;p&gt;So!  These are some other factors that could help or hurt a proposal:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Two accept-worthy proposals&lt;/strong&gt;.  We have budget for ~40 non-PowerShell.org speakers.  We have 56 or so speaking slots.  This means that we &lt;em&gt;need&lt;/em&gt; several folks to give more than one presentation.  If you submitted two strong proposals, chances are you got in, even if we only accepted one (we might have added your other as a potential backup).  I know this isn’t optimal, but we have a cap on attendees, so every speaker means less $$ to support the summit&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;We need a balance of known and unknown speakers&lt;/strong&gt;.  Known speakers can help sell tickets, and we know they’ll likely give solid sessions.  That said, we want to encourage folks to share and speak, so we want new folks as well!&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;We want to introduce more non-PowerShell-specific material&lt;/strong&gt;.  As folks progress in their careers, PowerShell will likely become less of a central focus.  It will still play a large part of this conference, but we’ll be including sessions on important topics that a PowerShell-er might run into as they progress in their career, even if those topics don’t involve PowerShell itself&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;We want to balance content&lt;/strong&gt; We mention it above, but again, we need to balance content.  We can’t have 20 Release Pipeline talks, and 10 Azure talks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s summarize these&lt;/p&gt;

&lt;h3 id=&quot;so-you-want-to-speak-at-the-summit&quot;&gt;So you want to speak at the summit&lt;/h3&gt;

&lt;p&gt;That was too many words.  Let’s boil it down to some takeaways that might help you with a successful proposal:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Do submit a proposal&lt;/strong&gt;.  Seriously.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Do give it a small bit of effort&lt;/strong&gt;.  Write it.  Review it.  Give it to someone else to review.  Adjust as needed&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Be wary of over-populated topics&lt;/strong&gt;.  These can be hard to judge.  You can always ask!!&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Do make your proposal stand out&lt;/strong&gt;.  It needs to capture our curiosity or interest&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Consider making it unique&lt;/strong&gt;.  Pester?  It’s been done.  Pester-for-some-unique-thing?  Much more likely&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Consider keeping it widely applicable&lt;/strong&gt;.  Even if you use a particular stack for your demos, let us know you’ll include widely applicable takeaways&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Consider interesting topics&lt;/strong&gt;.  This depends on the reviewer and the target audience, but is worth considering&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Do submit as early as possible&lt;/strong&gt;.  You’ll stand out more initially&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Be wary of constrained topics&lt;/strong&gt;.  We’re not going to have a conference of all Azure or AWS bits&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Do include enough information&lt;/strong&gt; for us and your audience to know what you’ll be talking about, even if it’s a very rough outline&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Do submit more than one proposal&lt;/strong&gt;.  Don’t throw everything against the wall, but don’t risk your single proposal getting unlucky&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Be wary of repeat topics&lt;/strong&gt;.  At the very least, if you’ve done it before, let us know how it has changed&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Do keep the topic appropriate&lt;/strong&gt; for this conference.  Not sure?  Just ask!&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Do get involved with the community&lt;/strong&gt; if you can.  Blog,  share / open source your work, attend and speak at user groups, do a &lt;a href=&quot;https://github.com/PSPowerHour/PSPowerHour&quot;&gt;PSPowerHour&lt;/a&gt;, etc.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Do ask for feedback&lt;/strong&gt;.  We created #Conferences in the &lt;a href=&quot;https://bit.ly/psslack&quot;&gt;PowerShell Slack team&lt;/a&gt;.  I suspect this would have helped on a number of bullets above, and for a number of proposals&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Okay!  Let’s talk about the CFP itself&lt;/p&gt;

&lt;h2 id=&quot;some-cfp-stats&quot;&gt;Some CFP Stats&lt;/h2&gt;

&lt;p&gt;We have a bunch of data on the CFP proposals.  Nothing particularly interesting, but if you’re curious:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;We had something like &lt;strong&gt;205 proposals&lt;/strong&gt;.  We actually had to send back a few ahead of the deadline, to make sure folks weren’t locked out by the 200 cap&lt;/li&gt;
  &lt;li&gt;We had &lt;strong&gt;56 accepted proposals&lt;/strong&gt;.  46 standard (45 min) sessions, 10 double (90 min) sessions&lt;/li&gt;
  &lt;li&gt;We have &lt;strong&gt;21 new speakers, and 19 returning speakers&lt;/strong&gt;.  This is in relation to this specific summit.  Perhaps 5 to 10 of the new speakers speak at other PowerShell-y conferences&lt;/li&gt;
  &lt;li&gt;We have something like &lt;strong&gt;11 Microsoft MVPs&lt;/strong&gt;.  This doesn’t include folks like me, who aren’t speaking unless we get pulled in as a last minute backup, or the folks like Don who are speaking only at the OnRamp track.  Also, the whole MVP thing?  It can be nonsense.  We have folks like Brandon Olin and Mathias Jessen who… aren’t… MVPs.  Right&lt;/li&gt;
  &lt;li&gt;We have &lt;strong&gt;40 total unique speakers&lt;/strong&gt;.  Only one is from PowerShell.org (James Petty).  I’d &lt;em&gt;much&lt;/em&gt; rather listen to you all speak, than to share myself, even if you took the &lt;em&gt;I-get-nervous-before-speaking&lt;/em&gt; and &lt;em&gt;it-takes-time-to-prepare-a-talk&lt;/em&gt; factors our of the equation&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;75 unique speakers didn’t make it&lt;/strong&gt;.  This was seriously painful.  I know many of them from the community, and have met a good number of them in person.  I encouraged many of them to propose.  This was the hardest part of the CFP for me&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;10 Microsoft MVPs didn’t make it&lt;/strong&gt;, along with several Microsoft folks, and a number of other awesome folks in the community&lt;/li&gt;
  &lt;li&gt;There’s some overlap given that proposals can cover more than one topic, but looking at a boiled down &lt;em&gt;what was the primary topic&lt;/em&gt;, the three most popular topics were:
    &lt;ul&gt;
      &lt;li&gt;&lt;strong&gt;Release Pipeline - 20 proposals&lt;/strong&gt;&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Azure proposals - 13 proposals&lt;/strong&gt;&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Pester proposals - 11 proposals&lt;/strong&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;We had some prolific proposers.  I can’t imagine coming up with 9 ideas and enough to propose on each.  Two speakers did&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;how-did-you-work-on-the-cfp&quot;&gt;How did you work on the CFP?&lt;/h2&gt;

&lt;p&gt;So!  This was my first time being responsible (well, co-responsible!) for a CFP.  Some notes for posterity:&lt;/p&gt;

&lt;h3 id=&quot;things-that-worked&quot;&gt;Things that worked&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Google Sheets&lt;/strong&gt;.  PaperCall and other services might not have the best reviewing tools.  Some easy-to-collaborate spreadsheet service works for this&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Having help&lt;/strong&gt;.  Don’t go alone.  Seriously.  IMHO no CFP should be handled by a single person.  The stress, items you might miss without a second pair of eyes, and other issues will pile up.  Thanks Missy : D&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Knowing spreadsheet language basics&lt;/strong&gt;.  Using spreadsheets?  Learn the language (functions, formulas, etc.).  There’s only so much manual work you can do.  Sheets and Excel both have useful query languages, and you should know enough to pick these up without too much trouble&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Keeping identifier data&lt;/strong&gt; to tie review data to CFP data.  Make sure each review entry has enough data to uniquely tie it back to a talk - for example, the submitter’s name or e-mail and the title of a talk.  Watch out for whitespace.  This helps reconcile the data if you need to join it up later on&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Using PowerShell&lt;/strong&gt;!  Once we had review data in sheets, and proposal data in PaperCall, it was easy to &lt;a href=&quot;http://ramblingcookiemonster.github.io/Join-Object/&quot;&gt;join&lt;/a&gt;, filter, etc., generate speaker agreement word documents, and send acceptance, back-up, and not-selected &lt;a href=&quot;https://stackoverflow.com/a/51158377/3067642&quot;&gt;e-mails via smtp&lt;/a&gt;.  I also had a quick way to generate &lt;a href=&quot;https://twitter.com/psCookieMonster/lists/pshsummit-2019-speakers&quot;&gt;all the speaker’s twitter handles&lt;/a&gt;, stats for this article, etc.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Encouraging people&lt;/strong&gt;.  I can’t tell you how many folks I pinged, who either hadn’t considered proposing, figured they didn’t have anything worth proposing, just didn’t see that we were running a CFP, etc.  A number of these folks were selected.  I’ll remind folks again next year, but seriously, &lt;em&gt;if something helps you or your teammates, chances are it might help folks at this conference&lt;/em&gt;.  Propose it!
    &lt;ul&gt;
      &lt;li&gt;Side note:  if I missed pinging you, I’m sorry!  tried to get a bunch of folks, but I missed plenty&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Remote collaboration tools&lt;/strong&gt;.  Already mentioned Google Sheets.  Slack and screen-sharing-tools (e.g. Skype) also came in quite handy when working with other reviewers&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;things-that-didnt-work&quot;&gt;Things that didn’t work&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Manual work&lt;/strong&gt;.  Each time a new proposal came in, we manually copied data over to our review sheet.  This was painful
    &lt;ul&gt;
      &lt;li&gt;Suggestion: have an easy process to add proposal metadata to your review data, without messing with existing review info.  For example, download your review data (e.g. sheets), download your PaperCall data, join it, and publish the data back to Sheets.  Maybe use &lt;a href=&quot;https://github.com/scrthq/PSGSuite&quot;&gt;existing tools&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Not linking review data to CFP data&lt;/strong&gt;.  We used google sheets for review.  Each time we needed to review proposal content, we needed to browse to PaperCall, search (its search tool is horrible), select, and scroll to the content
    &lt;ul&gt;
      &lt;li&gt;Suggestion:  Make it easy to tie your review metadata and notes to the CFP data.  A link to the CFP proposal for each row in your review, for example&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Too many Maybes&lt;/strong&gt;.  We each rated a talk as it came in.  Yes, No, Maybe.  I think we (I did, at least) might have put too many in the &lt;em&gt;Maybe&lt;/em&gt; pile.  We revisited the &lt;em&gt;Maybe&lt;/em&gt; pile multiple times, but not too many proposals went from &lt;em&gt;Maybe&lt;/em&gt; to &lt;em&gt;Yes&lt;/em&gt;, and I worry that we missed some gems
    &lt;ul&gt;
      &lt;li&gt;Suggestion:  Be as thorough as possible when you first rate a proposal.  Don’t look at a proposal if you don’t have time to review it in full.  Try to avoid marking proposals &lt;em&gt;Maybe&lt;/em&gt; unless you really mean it.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Tags, not buckets&lt;/strong&gt;.  We tried to boil down each talk into a bucket (e.g. Release Pipeline, Azure, Serverless).  Here’s the thing:  Many talks might have two or more topics involved, and might include a constrained topic.  For example, Release Pipelines overlapped heavily with Infrastructure-As-Code, and the Serverless bucket often included a constrained topic (Azure or AWS)
    &lt;ul&gt;
      &lt;li&gt;Tag central topics for each session.  Submitters often do this, but they might be overzealous, or use different words - normalize and filter these.  Spreadsheets aren’t the best for many-to-many relationships, but you can shoehorn it in&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I’m sure there’s more, but this is mostly a quick brain dump for next year’s CFP&lt;/p&gt;

&lt;p&gt;Whew!  That’s it!  Tickets go on sale November 1, and you can find a brochure, agenda, and other links &lt;a href=&quot;https://powershell.org/summit/&quot;&gt;here&lt;/a&gt;. &lt;a href=&quot;http://bit.ly/summit2019agenda&quot;&gt;The initial agenda looks fantastic&lt;/a&gt; (I know, I’m biased).  Hope to see you at the summit!&lt;/p&gt;

    &lt;p&gt;&lt;a href=&quot;http://ramblingcookiemonster.github.io/Summit-CFP/&quot;&gt;The 2019 PowerShell and DevOps Global Summit CFP&lt;/a&gt; was originally published by Warren F at &lt;a href=&quot;http://ramblingcookiemonster.github.io&quot;&gt;Rambling Cookie Monster&lt;/a&gt; on October 16, 2018.&lt;/p&gt;
  </content>
</entry>


<entry>
  <title type="html"><![CDATA[Quick hit&#58; PoshBot Useability]]></title>
  <link rel="alternate" type="text/html" href="http://ramblingcookiemonster.github.io/PoshBot-Use/" />
  <id>http://ramblingcookiemonster.github.io/PoshBot-Use</id>
  <updated>2018-09-13T07:00:00+00:00</updated>
  <published>2018-09-13T07:00:00+00:00</published>
  
  <author>
    <name>Warren F</name>
    <uri>http://ramblingcookiemonster.github.io</uri>
    
  </author>
  <content type="html">
    &lt;section id=&quot;table-of-contents&quot; class=&quot;toc&quot;&gt;
  &lt;header&gt;
    &lt;h3&gt;&lt;i class=&quot;fa fa-book&quot;&gt;&lt;/i&gt; Overview&lt;/h3&gt;
  &lt;/header&gt;
&lt;div id=&quot;drawer&quot;&gt;
&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#rambling&quot; id=&quot;markdown-toc-rambling&quot;&gt;Rambling&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#isnt-poshbot-just-powershell&quot; id=&quot;markdown-toc-isnt-poshbot-just-powershell&quot;&gt;Isn’t PoshBot Just PowerShell?&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#no-tab-completion&quot; id=&quot;markdown-toc-no-tab-completion&quot;&gt;No Tab Completion&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#use-aliases&quot; id=&quot;markdown-toc-use-aliases&quot;&gt;Use aliases&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#keep-it-short&quot; id=&quot;markdown-toc-keep-it-short&quot;&gt;Keep it short&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#keep-it-consistent&quot; id=&quot;markdown-toc-keep-it-consistent&quot;&gt;Keep it consistent&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#make-it-easy&quot; id=&quot;markdown-toc-make-it-easy&quot;&gt;Make it easy&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#give-good-examples&quot; id=&quot;markdown-toc-give-good-examples&quot;&gt;Give good examples&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#no-pipeline&quot; id=&quot;markdown-toc-no-pipeline&quot;&gt;No Pipeline&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#do-everything-in-your-command&quot; id=&quot;markdown-toc-do-everything-in-your-command&quot;&gt;Do everything in your command&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#format-things-for-the-user&quot; id=&quot;markdown-toc-format-things-for-the-user&quot;&gt;Format things for the user&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#output-needs-more-consideration&quot; id=&quot;markdown-toc-output-needs-more-consideration&quot;&gt;Output Needs More Consideration&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#pick-sane-defaults&quot; id=&quot;markdown-toc-pick-sane-defaults&quot;&gt;Pick sane defaults&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#dont-spam&quot; id=&quot;markdown-toc-dont-spam&quot;&gt;Don’t spam&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#thats-a-whole-bunch-of-work&quot; id=&quot;markdown-toc-thats-a-whole-bunch-of-work&quot;&gt;That’s a Whole Bunch of Work!&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#is-there-an-easy-button&quot; id=&quot;markdown-toc-is-there-an-easy-button&quot;&gt;Is there an easy button?&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#youre-wrong&quot; id=&quot;markdown-toc-youre-wrong&quot;&gt;You’re wrong!&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

  &lt;/div&gt;
&lt;/section&gt;
&lt;!-- /#table-of-contents --&gt;

&lt;h2 id=&quot;rambling&quot;&gt;Rambling&lt;/h2&gt;

&lt;p&gt;So!  First post in a year.  Guess setting up the &lt;em&gt;Quick Hits&lt;/em&gt; section was totally worth it :)&lt;/p&gt;

&lt;p&gt;It’s been a great year!  On top of various fun with PowerShell, keeping up with a two-year-old destroyer-of-all-the-things, and having a new destroyer-of-all-the-things in training, I’ve spent a good bit of time exposing handy tools in Slack via &lt;a href=&quot;https://github.com/poshbotio/PoshBot&quot;&gt;PoshBot&lt;/a&gt;, an awesome bot framework by &lt;a href=&quot;https://twitter.com/devblackops&quot;&gt;Brandon Olin&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We’ve been using PoshBot for over a year (see: &lt;a href=&quot;http://ramblingcookiemonster.github.io/PoshBot/&quot;&gt;last post&lt;/a&gt;), and we finally have Microsoft Teams support in PoshBot - figured I’d try to lay out some practices and tooling I’ve found helpful in case it would help anyone!&lt;/p&gt;

&lt;h2 id=&quot;isnt-poshbot-just-powershell&quot;&gt;Isn’t PoshBot Just PowerShell?&lt;/h2&gt;

&lt;p&gt;Nope.  Not.  At.  All.  Let’s start out with what we don’t have:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;No tab completion&lt;/strong&gt;.  Quick discovery and command/parameter validation simply isn’t a thing&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;No pipeline&lt;/strong&gt;.  Yet&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Output needs more consideration&lt;/strong&gt;.  Unless you want to make your rooms unreadable&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So!  What does this mean?  How can we make this more usable?&lt;/p&gt;

&lt;h2 id=&quot;no-tab-completion&quot;&gt;No Tab Completion&lt;/h2&gt;

&lt;p&gt;Get ready for co-workers who can’t remember, or mistype commands and parameters all the time.  Heck, I write most of our plugins, and do this all the time.  What can we do about it?&lt;/p&gt;

&lt;h3 id=&quot;use-aliases&quot;&gt;Use aliases&lt;/h3&gt;

&lt;p&gt;How do I force AD replication?  &lt;code class=&quot;highlighter-rouge&quot;&gt;!syncad&lt;/code&gt;?  &lt;code class=&quot;highlighter-rouge&quot;&gt;!adsync&lt;/code&gt;?  Something else?  Here’s the thing:  aliases are free.  If you see a very common mistyped variant, and if it makes sense and won’t be confusing now or when you add more commands, go ahead and add it!&lt;/p&gt;

&lt;h3 id=&quot;keep-it-short&quot;&gt;Keep it short&lt;/h3&gt;

&lt;p&gt;Verbosity is king in PowerShell.  With no tab completion to help discovery and spelling?  Keep it short!  Reserve single character aliases for your most popular commands and parameters.  Expand as needed.  Again, you can always leave the actual command name in a PowerShell &lt;code class=&quot;highlighter-rouge&quot;&gt;Verb-Noun&lt;/code&gt; format, but do use short aliases for chat.&lt;/p&gt;

&lt;p&gt;Don’t forget parameters!  Want to specify properties?  &lt;code class=&quot;highlighter-rouge&quot;&gt;-p&lt;/code&gt; is a nice shorthand for &lt;code class=&quot;highlighter-rouge&quot;&gt;-property&lt;/code&gt;, for example.&lt;/p&gt;

&lt;h3 id=&quot;keep-it-consistent&quot;&gt;Keep it consistent&lt;/h3&gt;

&lt;p&gt;This goes for PowerShell as well.  If you have a parameter that offers the same functionality in many commands, be sure you also give it the same name and aliases in every command.  With no tab completion, this simplifies memorization, and lets us extrapolate syntax and naming from other commands.&lt;/p&gt;

&lt;h3 id=&quot;make-it-easy&quot;&gt;Make it easy&lt;/h3&gt;

&lt;p&gt;A busy command definition is much nicer than forcing a user to run a busy command.  Abstract things out.  For example…&lt;/p&gt;

&lt;p&gt;You want to query users from Active Directory.  What are some common criteria?  Not everyone will want to type raw ldap queries in chat.  Can you inspect parameter values and make assumptions (clarifying in the help for each parameter)?  For example:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;!u wframe&lt;/code&gt;: Get users with identity wframe&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;!u wframe@g.something.edu&lt;/code&gt;:  Get users with mail wframe@g.something.edu&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;!u 12345&lt;/code&gt;:  Get users by UID&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;!u 'warren frame'&lt;/code&gt;:  There’s a space!  Use &lt;a href=&quot;https://social.technet.microsoft.com/wiki/contents/articles/22653.active-directory-ambiguous-name-resolution.aspx&quot;&gt;ambiguous name resolution&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Positional parameters are also quite helpful, for your most commonly used parameters.&lt;/p&gt;

&lt;h3 id=&quot;give-good-examples&quot;&gt;Give good examples&lt;/h3&gt;

&lt;p&gt;Comment based help examples should already be the norm, but be sure to do a solid job showcasing these - I tend to use full command/parameter names in the first example, and the easiest shorthand thereafter.  Reading help is a bit more painful in chat.  Examples give you a quick way to say &lt;em&gt;ah, this is exactly what I need to do!&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;no-pipeline&quot;&gt;No Pipeline&lt;/h2&gt;

&lt;h3 id=&quot;do-everything-in-your-command&quot;&gt;Do everything in your command&lt;/h3&gt;

&lt;p&gt;So!  Forget the Monad Manifesto and PowerShell best practices for now.  You can’t pipe.  Want specific properties back?  Include a &lt;code class=&quot;highlighter-rouge&quot;&gt;-Property&lt;/code&gt; (&lt;code class=&quot;highlighter-rouge&quot;&gt;-p&lt;/code&gt;) parameter.  Want to format data as a table, list, csv, or something else?  Include a &lt;code class=&quot;highlighter-rouge&quot;&gt;-Format&lt;/code&gt; (&lt;code class=&quot;highlighter-rouge&quot;&gt;-f&lt;/code&gt;) parameter.  Think about other common needs.&lt;/p&gt;

&lt;p&gt;Once you gather up a common group of parameters, strongly consider adding them to every PohBot command they apply to - no output on a command?  Yeah, probably don’t need &lt;code class=&quot;highlighter-rouge&quot;&gt;-Format&lt;/code&gt; (&lt;code class=&quot;highlighter-rouge&quot;&gt;-f&lt;/code&gt;) on that one.&lt;/p&gt;

&lt;h3 id=&quot;format-things-for-the-user&quot;&gt;Format things for the user&lt;/h3&gt;

&lt;p&gt;So!  We just mentioned this.  We can’t just pipe to &lt;code class=&quot;highlighter-rouge&quot;&gt;Format-Table&lt;/code&gt; or &lt;code class=&quot;highlighter-rouge&quot;&gt;Export-Csv&lt;/code&gt; in chat.  You probably want some common format options via &lt;code class=&quot;highlighter-rouge&quot;&gt;-Format&lt;/code&gt; (&lt;code class=&quot;highlighter-rouge&quot;&gt;-f&lt;/code&gt;).  Tables.  Lists.  CSVs.  Pretty spreadsheets with tables.  No one wants a command that spams a room with unnecessary lines of text or whitespace - give folks only what they need.&lt;/p&gt;

&lt;p&gt;Be opinionated!  For example…&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Did they use wildcards or some other fuzzy search and get a bunch of results?  Bundle it into a CSV, or into a table with only the properties they would need to get the details to query further (e.g. &lt;code class=&quot;highlighter-rouge&quot;&gt;userprincipalname&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;displayname&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;mail&lt;/code&gt; for an AD user)&lt;/li&gt;
  &lt;li&gt;Is the text-based representation of the output longer than you want in the channel (e.g. &amp;gt; 8000 characters)?  Switch &lt;code class=&quot;highlighter-rouge&quot;&gt;Format&lt;/code&gt; over to something like a CSV, which Slack does not truncate&lt;/li&gt;
  &lt;li&gt;Is the output a single item with a single property on it?  Expand it!  Do include the property name to help tie the output back to the command&lt;/li&gt;
  &lt;li&gt;Is there something abnormal in the output that should be highlighted?  Consider doing so!  Are there disabled users in your output?  Consider listing them out (alphabetically of course) in a warning, ahead of the command output&lt;/li&gt;
  &lt;li&gt;Is there output that generally isn’t needed?  Consider excluding it by default, and adding a switch to override - (e.g. AD group membership data might exclude disabled users by default)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;output-needs-more-consideration&quot;&gt;Output Needs More Consideration&lt;/h2&gt;

&lt;h3 id=&quot;pick-sane-defaults&quot;&gt;Pick sane defaults&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Get-ADUser&lt;/code&gt; gives me back DistinguishedName, Enabled, GivenName, Name, ObjectClass, ObjectGUID, SamAccountName, SID, Surname, and UserPrincipalName.  Different folks have different needs, but I’m guessing most of us don’t care about &lt;em&gt;most&lt;/em&gt; of those properties.  Including them in chat, with it’s limited character width and ugly wrapping behavior, would be painful.&lt;/p&gt;

&lt;p&gt;Pick sane properties to return by default, and allow the user to specify more properties (ideally, support &lt;code class=&quot;highlighter-rouge&quot;&gt;-Property *&lt;/code&gt; to allow discovery).  Pick a sane property to sort on by default, if that makes sense.  Pick a default format (e.g. table, csv, list) based on the most common use of a command.&lt;/p&gt;

&lt;p&gt;Be sure to consider your chat system’s line wrapping.  Using Slack message attachments (common)?  You get ~78 characters.  Can you get away with a table?  Will some data make your output unreadable?  You can always adjust things as you see issues pop up in chat.&lt;/p&gt;

&lt;h3 id=&quot;dont-spam&quot;&gt;Don’t spam&lt;/h3&gt;

&lt;p&gt;Just don’t.  Already mentioned this, but be sure to inspect your output.  Recent PSSlack changes added support to handle rate limiting, but still, you don’t want to destroy the usability of whatever channel you’re chatting in!&lt;/p&gt;

&lt;p&gt;There are many ways to do this.  I tend to globally say &lt;em&gt;if the string output is longer than 8000 characters, you get a CSV or whatever file type is appropriate&lt;/em&gt;.  Slack truncates messages.  Larger items fit much more nicely in a file&lt;/p&gt;

&lt;p&gt;Also, be careful with errors.  PoshBot will pick up errors and send them to chat.  Any intentional manipulation of output you do is completely ignored at that point.  Let me get this big list of group members!  Oops.  Formatting error on each of the 100 members that came back.  Slightly embarrassing : )&lt;/p&gt;

&lt;h2 id=&quot;thats-a-whole-bunch-of-work&quot;&gt;That’s a Whole Bunch of Work!&lt;/h2&gt;

&lt;p&gt;Perhaps.  Most of this is just thinking about how folks will use the tools you write, and accommodating a platform that doesn’t come with the niceties of a CLI.&lt;/p&gt;

&lt;p&gt;Just consider:  If you don’t spend a little time thinking about this, you might lose out on the benefits of ChatOps - folks might start simply using direct messages instead of actually working in-line with chat.  Some folks might get frustrated if you have inconsistent parameter names, or only a single command name that they forget every time.&lt;/p&gt;

&lt;p&gt;Also… I don’t know about you, but I love convenience.  Much of this is bad practice in PowerShell, given that we can use everything in the pipeline, but here you can get as opinionated as you’d like, while still doing the right thing and making life easier for you&lt;/p&gt;

&lt;h3 id=&quot;is-there-an-easy-button&quot;&gt;Is there an easy button?&lt;/h3&gt;

&lt;p&gt;Not really.  Most of this will become apparent as you use PoshBot.  Just start using PoshBot.  See what works.  Ask around to see what you can improve.  Don’t turn your nose up at ideas that fly in the face of PowerShell best practices - ChatOps is a different context.&lt;/p&gt;

&lt;p&gt;I’ve written a module that can make things a little easier, but it’s ugly, and might not fit your needs.&lt;/p&gt;

&lt;p&gt;I tend to write all of my functions with a set of their own &lt;em&gt;common parameters&lt;/em&gt; like &lt;code class=&quot;highlighter-rouge&quot;&gt;-Format&lt;/code&gt; or &lt;code class=&quot;highlighter-rouge&quot;&gt;-Property&lt;/code&gt;.  I then pass any output from a PoshBot command to &lt;code class=&quot;highlighter-rouge&quot;&gt;ConvertTo-PoshBotResponse&lt;/code&gt;, a function that knows how to parse these common parameters, checks for lengthy output and switching to CSV, and other niceties.&lt;/p&gt;

&lt;p&gt;I’ll publish the code and write a short bit on this soon!&lt;/p&gt;

&lt;h3 id=&quot;youre-wrong&quot;&gt;You’re wrong!&lt;/h3&gt;

&lt;p&gt;What do you think?  Do these practices make sense for bot commands?  Do you have other good practices to follow?  Are any of my suggestions misguided?  I’ll try to include a summation in my next post!&lt;/p&gt;

&lt;p&gt;That’s it for today!  It’s been a while so I’ll mention a few things I think you should check out:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://twitter.com/barbariankb&quot;&gt;Michael Lombardi&lt;/a&gt; and I recently started &lt;a href=&quot;https://github.com/PSPowerHour/PSPowerHour&quot;&gt;PSPowerHour&lt;/a&gt;.  Basically, 8 people do lightning demos on something fun.  It’s a great, low-pressure way to show off something fun and get experience speaking.  Do submit a proposal!&lt;/li&gt;
  &lt;li&gt;I’m working with &lt;a href=&quot;https://twitter.com/thedevopsdiva&quot;&gt;Missy Januszko&lt;/a&gt; on content for the 2019 PowerShell + DevOps Global Summit - We have some awesome sessions lined up, but need more - do consider &lt;a href=&quot;https://powershell.org/2018/09/01/getting-feedback-on-powershell-devops-global-summit-proposals/&quot;&gt;submitting a proposal&lt;/a&gt;!&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://twitter.com/mikefrobbins&quot;&gt;Mike Robbins&lt;/a&gt; put together an amazing PowerShell book &lt;a href=&quot;https://twitter.com/barbariankb/status/1015273736763822080&quot;&gt;for a good cause&lt;/a&gt;.  It’s available on &lt;a href=&quot;https://leanpub.com/powershell-conference-book&quot;&gt;leanpub&lt;/a&gt; and now &lt;a href=&quot;https://www.amazon.com/dp/1720169977/&quot;&gt;Amazon&lt;/a&gt; - do check this out!&lt;/li&gt;
&lt;/ul&gt;

    &lt;p&gt;&lt;a href=&quot;http://ramblingcookiemonster.github.io/PoshBot-Use/&quot;&gt;Quick hit&amp;#58; PoshBot Useability&lt;/a&gt; was originally published by Warren F at &lt;a href=&quot;http://ramblingcookiemonster.github.io&quot;&gt;Rambling Cookie Monster&lt;/a&gt; on September 13, 2018.&lt;/p&gt;
  </content>
</entry>


<entry>
  <title type="html"><![CDATA[Getting Started with PoshBot]]></title>
  <link rel="alternate" type="text/html" href="http://ramblingcookiemonster.github.io/PoshBot/" />
  <id>http://ramblingcookiemonster.github.io/PoshBot</id>
  <updated>2017-06-25T07:00:00+00:00</updated>
  <published>2017-06-25T07:00:00+00:00</published>
  
  <author>
    <name>Warren F</name>
    <uri>http://ramblingcookiemonster.github.io</uri>
    
  </author>
  <content type="html">
    &lt;section id=&quot;table-of-contents&quot; class=&quot;toc&quot;&gt;
  &lt;header&gt;
    &lt;h3&gt;&lt;i class=&quot;fa fa-book&quot;&gt;&lt;/i&gt; Overview&lt;/h3&gt;
  &lt;/header&gt;
&lt;div id=&quot;drawer&quot;&gt;
&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#rambling&quot; id=&quot;markdown-toc-rambling&quot;&gt;Rambling&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#why-chatops-why-bots&quot; id=&quot;markdown-toc-why-chatops-why-bots&quot;&gt;Why ChatOps? Why Bots?&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#poshbot&quot; id=&quot;markdown-toc-poshbot&quot;&gt;PoshBot&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#create-a-slack-bot-user&quot; id=&quot;markdown-toc-create-a-slack-bot-user&quot;&gt;Create a Slack bot user&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#configuring-poshbot&quot; id=&quot;markdown-toc-configuring-poshbot&quot;&gt;Configuring PoshBot&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#run-poshbot-as-a-service&quot; id=&quot;markdown-toc-run-poshbot-as-a-service&quot;&gt;Run PoshBot as a service&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#write-a-poshbot-plugin&quot; id=&quot;markdown-toc-write-a-poshbot-plugin&quot;&gt;Write a PoshBot plugin&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#using-poshbot&quot; id=&quot;markdown-toc-using-poshbot&quot;&gt;Using PoshBot&lt;/a&gt;        &lt;ul&gt;
          &lt;li&gt;&lt;a href=&quot;#poshbot-access-controls&quot; id=&quot;markdown-toc-poshbot-access-controls&quot;&gt;PoshBot Access Controls&lt;/a&gt;&lt;/li&gt;
          &lt;li&gt;&lt;a href=&quot;#getting-help&quot; id=&quot;markdown-toc-getting-help&quot;&gt;Getting Help&lt;/a&gt;&lt;/li&gt;
          &lt;li&gt;&lt;a href=&quot;#running-commands&quot; id=&quot;markdown-toc-running-commands&quot;&gt;Running Commands&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#whats-next&quot; id=&quot;markdown-toc-whats-next&quot;&gt;What’s Next?&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#we-dont-trust-bots&quot; id=&quot;markdown-toc-we-dont-trust-bots&quot;&gt;We Don’t Trust Bots&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#references&quot; id=&quot;markdown-toc-references&quot;&gt;References&lt;/a&gt;        &lt;ul&gt;
          &lt;li&gt;&lt;a href=&quot;#what-about-poshbot-itself&quot; id=&quot;markdown-toc-what-about-poshbot-itself&quot;&gt;What about PoshBot itself?&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

  &lt;/div&gt;
&lt;/section&gt;
&lt;!-- /#table-of-contents --&gt;

&lt;h2 id=&quot;rambling&quot;&gt;Rambling&lt;/h2&gt;

&lt;p&gt;One of my favorite things about events like the &lt;a href=&quot;https://powershell.org/summit/&quot;&gt;PowerShell + DevOps Global Summit&lt;/a&gt; is the opportunity to chat with so many folks in the PowerShell community.  Invariably, you end up bringing back a wealth of &lt;a href=&quot;http://ramblingcookiemonster.github.io/RabbitMQ-Intro/&quot;&gt;fun ideas&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This year, &lt;a href=&quot;https://twitter.com/devblackops&quot;&gt;Brandon Olin&lt;/a&gt; gave an awesome lightning demo on &lt;a href=&quot;https://github.com/poshbotio/PoshBot&quot;&gt;PoshBot&lt;/a&gt;, a PowerShell-based bot framework.  It’s not quite as mature as &lt;a href=&quot;https://hodgkins.io/chatops-with-powershell-and-errbot&quot;&gt;Errbot&lt;/a&gt; or &lt;a href=&quot;https://hodgkins.io/chatops-on-windows-with-hubot-and-powershell&quot;&gt;Hubot&lt;/a&gt;, but it’s a super quick way to get a bot up and running for folks using PowerShell!&lt;/p&gt;

&lt;h2 id=&quot;why-chatops-why-bots&quot;&gt;Why ChatOps? Why Bots?&lt;/h2&gt;

&lt;p&gt;I’ll let someone more well versed in the topic handle this - Jason Hand has a great &lt;a href=&quot;https://www.youtube.com/watch?v=F8Vfoz7GeHw&quot;&gt;beginners guide to ChatOps&lt;/a&gt;.  Long story short?&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Communication - avoid certain folks monopolizing discussions, allow quiet folks to get important details out, keep everything in a searchable medium for new and existing team members&lt;/li&gt;
  &lt;li&gt;Transparency - information isn’t hidden in e-mail threads that new or excluded folks will never see - everything is searchable, and ideally you do work in open channels&lt;/li&gt;
  &lt;li&gt;Sharing - tools and ideas can be shared in a single spot&lt;/li&gt;
  &lt;li&gt;Learning - find details on old topics that never made it into docs, see how people solve things with bots&lt;/li&gt;
  &lt;li&gt;Automated communication - changes to code, deployments, alerts, etc. can be sent to chat&lt;/li&gt;
  &lt;li&gt;Security - Guardrails and other safeguards can reduce risk (consider what JEA provides)&lt;/li&gt;
  &lt;li&gt;Incident response - keep incident communication in central, searchable location, not a bunch of incomprehensible e-mail threads&lt;/li&gt;
  &lt;li&gt;Provide an interesting balance between creating a web interface or GUI for a PowerShell script, and forcing folks to use PowerShell&lt;/li&gt;
  &lt;li&gt;Share gifs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I could be off base, but I also see this as a prerequisite for effective remote work.&lt;/p&gt;

&lt;p&gt;So!  Hopefully you’re interested in checking this out.  It helps if your team is already using something like Slack.&lt;/p&gt;

&lt;p&gt;Let’s dive in and start using PoshBot!&lt;/p&gt;

&lt;h2 id=&quot;poshbot&quot;&gt;PoshBot&lt;/h2&gt;

&lt;p&gt;We’re going to cover the basics to get up and running with PoshBot:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Create a Slack bot&lt;/li&gt;
  &lt;li&gt;Create a PoshBot configuration&lt;/li&gt;
  &lt;li&gt;Run PoshBot as a service&lt;/li&gt;
  &lt;li&gt;Write a PoshBot plugin&lt;/li&gt;
  &lt;li&gt;Use PoshBot&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This might seem like a lot of work, but the configuration and service are a one time thing - Writing plugins is just like writing PowerShell functions and modules!&lt;/p&gt;

&lt;h3 id=&quot;create-a-slack-bot-user&quot;&gt;Create a Slack bot user&lt;/h3&gt;

&lt;p&gt;This assumes you have admin privileges to your Slack team:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Add a new &lt;a href=&quot;https://api.slack.com/bot-users&quot;&gt;bot&lt;/a&gt; integration: &lt;code class=&quot;highlighter-rouge&quot;&gt;https://YOUR-TEAM-HERE.slack.com/services/new/bot&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Name the bot - we’ll use &lt;code class=&quot;highlighter-rouge&quot;&gt;testbot&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Optionally, pick an icon&lt;/li&gt;
  &lt;li&gt;Copy the resulting token for the bot config.  Keep this safe!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We have our token, let’s get a bot configured!&lt;/p&gt;

&lt;h3 id=&quot;configuring-poshbot&quot;&gt;Configuring PoshBot&lt;/h3&gt;

&lt;p&gt;There’s more you can configure, but we’ll start with the basics.  Keep in mind you might abstract this into your configuration management system, use infrastructure as code, etc.&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/93dc5de64cda86757b4324c9e3c1924b.js&quot;&gt; &lt;/script&gt;

&lt;p&gt;That’s it! We can quickly test this interactively: &lt;code class=&quot;highlighter-rouge&quot;&gt;Start-PoshBot -Configuration $pbc -Verbose&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;We should see the bot load up and start listening:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;VERBOSE: Creating new Slack backend instance
VERBOSE: Creating bot instance with name [testbot]
...
VERBOSE: [SlackBackend:LoadUsers] Adding user: REDACTED:wframe
...
VERBOSE: [Group: AddUser] Adding user [REDACTED] to [Admin]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If we look in Slack, we’ll see the bot in the list of users, but we still need to invite it to any channel we want to use it in - &lt;code class=&quot;highlighter-rouge&quot;&gt;/invite @testbot&lt;/code&gt;.  You can also DM the bot.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/poshbot/invite.png&quot; alt=&quot;invite&quot; /&gt;&lt;/p&gt;

&lt;p&gt;That’s it, we have a bot! Let’s set it up to run as a service.&lt;/p&gt;

&lt;h3 id=&quot;run-poshbot-as-a-service&quot;&gt;Run PoshBot as a service&lt;/h3&gt;

&lt;p&gt;There are several ways to run PowerShell as a service; We’re going to use &lt;a href=&quot;https://nssm.cc/&quot;&gt;nssm&lt;/a&gt; - it’s a bit simpler than some of the &lt;a href=&quot;https://msdn.microsoft.com/en-us/magazine/mt703436.aspx&quot;&gt;alternatives&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;First things first - we need the script that will run as a service - &lt;code class=&quot;highlighter-rouge&quot;&gt;C:\poshbot\start-poshbot.ps1&lt;/code&gt;.  This is pretty bare bones, you could do more:&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/ef4bd0649f29ff0c3feb965967586d75.js&quot;&gt; &lt;/script&gt;

&lt;p&gt;Basically, read the config, run Start-Poshbot.  Forever.  Okay!  Let’s run this as a service:&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/9f918f7f1a767575920b2665e3102c92.js&quot;&gt; &lt;/script&gt;

&lt;p&gt;If all went well, in a few seconds we should see &lt;code class=&quot;highlighter-rouge&quot;&gt;testbot&lt;/code&gt; online in our Slack team again!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/poshbot/online.png&quot; alt=&quot;online&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Now for the fun part.  Let’s write a quick PoshBot plugin!&lt;/p&gt;

&lt;h3 id=&quot;write-a-poshbot-plugin&quot;&gt;Write a PoshBot plugin&lt;/h3&gt;

&lt;p&gt;A &lt;a href=&quot;http://poshbot.readthedocs.io/en/latest/guides/plugins/&quot;&gt;PoshBot plugin&lt;/a&gt; is basically a PowerShell module with a few PoshBot bits.  We’ll skip &lt;a href=&quot;http://ramblingcookiemonster.github.io/Building-A-PowerShell-Module/&quot;&gt;the typical module scaffolding&lt;/a&gt; to keep our example simple.&lt;/p&gt;

&lt;p&gt;We’ll create our poshbot.example module in our plugins directory:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$null&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; mkdir C:\poshbot\plugins\poshbot.example -force
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Next, we’ll create a quick module manifest, &lt;code class=&quot;highlighter-rouge&quot;&gt;Poshbot.Example.psd1&lt;/code&gt;:&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/e6bcd526f8c48b7c37c37a683ac51e85.js&quot;&gt; &lt;/script&gt;

&lt;p&gt;The key bits are to require PoshBot, and while not mandatory, describing the permissions can be helpful.&lt;/p&gt;

&lt;p&gt;Finally, we’ll draft a quick module script, &lt;code class=&quot;highlighter-rouge&quot;&gt;Poshbot.Example.psm1&lt;/code&gt;:&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/4be0ab84a4937c77edc2b602e5415039.js&quot;&gt; &lt;/script&gt;

&lt;p&gt;Whew!&lt;/p&gt;

&lt;p&gt;The key bit here is the &lt;a href=&quot;http://poshbot.readthedocs.io/en/latest/guides/command-authorization/permissions/#association-permissions-with-commands&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;[PoshBot.BotCommand()]&lt;/code&gt; custom attribute&lt;/a&gt; - we use this to tell PoshBot what the command name is (if different from function name), permissions required to run this, and aliases that will also call this command.&lt;/p&gt;

&lt;p&gt;So,  that &lt;code class=&quot;highlighter-rouge&quot;&gt;!user&lt;/code&gt; command is pretty ugly.  Unfortunately, we’re no longer in PowerShell land, where we write commands that do one thing and do it well, using the pipeline to connect things together.&lt;/p&gt;

&lt;p&gt;Some things you might consider:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Add different format options.  A table is pretty and easy to digest, but won’t always cut it&lt;/li&gt;
  &lt;li&gt;Provide PowerShell-esque Verb-Noun names, but add aliases for everything.  Short commands and parameter names are important, when you don’t have tab completion or intellisense&lt;/li&gt;
  &lt;li&gt;If it makes sense, add the ability to specify what properties to output, and pick a sane default&lt;/li&gt;
  &lt;li&gt;If it makes sense, consider writing a file and uploading it to Slack (e.g. a graph png, a spreadsheet), or linking to external URLs&lt;/li&gt;
  &lt;li&gt;Publish your module to the PowerShell gallery with a name starting &lt;code class=&quot;highlighter-rouge&quot;&gt;Poshbot.&lt;/code&gt; - PoshBot will automatically find these when you use &lt;code class=&quot;highlighter-rouge&quot;&gt;!Find-Plugin&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We’ve written a PoshBot plugin - how do we use it?&lt;/p&gt;

&lt;h3 id=&quot;using-poshbot&quot;&gt;Using PoshBot&lt;/h3&gt;

&lt;p&gt;So!  We have a bot, it’s running as a service, and we’ve started writing some custom commands - let’s play!&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;install-plugin poshbot.example&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/poshbot/install.png&quot; alt=&quot;install&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;!user wframe&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/poshbot/oops.png&quot; alt=&quot;oops&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Oh.  We have to deal with &lt;a href=&quot;http://poshbot.readthedocs.io/en/latest/guides/command-authorization/overview/&quot;&gt;access controls&lt;/a&gt; first!&lt;/p&gt;

&lt;h4 id=&quot;poshbot-access-controls&quot;&gt;PoshBot Access Controls&lt;/h4&gt;

&lt;p&gt;We’ll create a group and role, tie the group to the role, tie permissions to the role, and add users to the group:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;!New-Group sysadmins
!New-Role sysadmins
!Add-GroupRole sysadmins sysadmins

!Add-RolePermission sysadmins poshbot.example:read
!Add-GroupUser sysadmins wframe
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/images/poshbot/newgroup.png&quot; alt=&quot;Group&quot; /&gt;
&lt;img src=&quot;/images/poshbot/newrole.png&quot; alt=&quot;Role&quot; /&gt;
&lt;img src=&quot;/images/poshbot/addgrouprole.png&quot; alt=&quot;GroupRole&quot; /&gt;
&lt;img src=&quot;/images/poshbot/addrolepermission.png&quot; alt=&quot;RolePermission&quot; /&gt;
&lt;img src=&quot;/images/poshbot/addgroupuser.png&quot; alt=&quot;Groupuser&quot; /&gt;&lt;/p&gt;

&lt;p&gt;And finally: &lt;code class=&quot;highlighter-rouge&quot;&gt;!user wframe&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/poshbot/user.png&quot; alt=&quot;user&quot; /&gt;&lt;/p&gt;

&lt;h4 id=&quot;getting-help&quot;&gt;Getting Help&lt;/h4&gt;

&lt;p&gt;How did we know how to do that?  Mostly reading docs, but you can also use the &lt;code class=&quot;highlighter-rouge&quot;&gt;!help&lt;/code&gt; command if you ever forget a command name or syntax:&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;!help&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/poshbot/help.png&quot; alt=&quot;help&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;!help example:user&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/poshbot/helpcommand.png&quot; alt=&quot;help user&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Note that we need to specify enough of a command for it to be unique before we get the full help, we can’t just use &lt;code class=&quot;highlighter-rouge&quot;&gt;user&lt;/code&gt;.&lt;/p&gt;

&lt;h4 id=&quot;running-commands&quot;&gt;Running Commands&lt;/h4&gt;

&lt;p&gt;We’re ready to go!  Some quick examples using our functions:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Use an arbitrary LDAP filter, and specify some specific properties to return:  &lt;code class=&quot;highlighter-rouge&quot;&gt;!user --l '(mail=wframe*)' --p samaccountname, displayname, enabled&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/images/poshbot/user-ldap.png&quot; alt=&quot;ldap filter&quot; /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Check out what variables are available when in a PoshBot function: &lt;code class=&quot;highlighter-rouge&quot;&gt;!var&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/images/poshbot/debug-var.png&quot; alt=&quot;debug var&quot; /&gt;&lt;/p&gt;

&lt;p&gt;That’s about it!  Now you know how to start up your own PoshBot in Slack, and write custom commands.&lt;/p&gt;

&lt;h2 id=&quot;whats-next&quot;&gt;What’s Next?&lt;/h2&gt;

&lt;p&gt;Many folks start with read-only commands until they get comfortable with things.  Some example tasks you might write commands for:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Get stuff from AD - groups, group membership, users, etc.&lt;/li&gt;
  &lt;li&gt;Do stuff in AD - disable or unlock accounts, kick off replication&lt;/li&gt;
  &lt;li&gt;Get stuff and do stuff from a variety of other systems!
    &lt;ul&gt;
      &lt;li&gt;Tickets&lt;/li&gt;
      &lt;li&gt;Configuration management systems&lt;/li&gt;
      &lt;li&gt;CMDBs or other inventory sources&lt;/li&gt;
      &lt;li&gt;IPAM systems&lt;/li&gt;
      &lt;li&gt;Monitoring systems&lt;/li&gt;
      &lt;li&gt;Databases&lt;/li&gt;
      &lt;li&gt;etc.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Restart services and servers&lt;/li&gt;
  &lt;li&gt;Kill stuck sessions&lt;/li&gt;
  &lt;li&gt;Query log data, performance data, etc. - this can be text, graph based, anything else (you can create and upload arbitrary files!)&lt;/li&gt;
  &lt;li&gt;Provide quick shortcuts to common docs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For all of these, you’ll need to consider a way to provide access to your service account.  Some common approaches:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;If possible, use a password manager, or serialize credentials via DPAPI so that your service account can read them and use them in commands&lt;/li&gt;
  &lt;li&gt;You might create delegated, constrained endpoints, or use JEA&lt;/li&gt;
  &lt;li&gt;You might run the service as an account that has delegated access (be extra cautious here), etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Consider read-only accounts, or delegating only the privileges needed for any approach you take.  Also, you might classify your system appropriately to ensure appropriate security controls are in place : )&lt;/p&gt;

&lt;p&gt;Unfortunately, some folks are apprehensive about bots. What if something breaks? What if someone breaks in?&lt;/p&gt;

&lt;h3 id=&quot;we-dont-trust-bots&quot;&gt;We Don’t Trust Bots&lt;/h3&gt;

&lt;p&gt;I’ve heard this.  I’m sure others will hear it as well.  Boiled down, PoshBot is pretty secure:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;There’s no opportunity for injection.  PoshBot doesn’t execute text, it uses the AST to validate commands and parameters&lt;/li&gt;
  &lt;li&gt;Only commands PoshBot knows about can run&lt;/li&gt;
  &lt;li&gt;PoshBot enforces access controls via groups, roles, and permissions&lt;/li&gt;
  &lt;li&gt;If an author considers &lt;a href=&quot;http://ramblingcookiemonster.github.io/Trust-but-Verify/&quot;&gt;what could go wrong&lt;/a&gt;, guardrails and other safeguards can make a command more safe than running natively - think about the benefits of using JEA.  Maybe I provide access to restart services, but only a subset of services that make sense&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are still a few risks.  What if Slack itself is compromised?  What if an account is compromised?  A few things to consider that would mitigate this a bit:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Use &lt;a href=&quot;https://get.slack.help/hc/en-us/articles/212221668-Require-two-factor-authentication-for-your-team&quot;&gt;two-factor auth&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Limit the extent of what your commands can do.  Maybe don’t write &lt;code class=&quot;highlighter-rouge&quot;&gt;delete datacenter&lt;/code&gt;, or allow &lt;code class=&quot;highlighter-rouge&quot;&gt;add innocent-looking-backup-account to domain admins&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Keep in mind security is about risk management, otherwise we would throw all our equipment into the sea and be done with computing.  For most, the value of ChatOps will far outweigh the risks.&lt;/p&gt;

&lt;h3 id=&quot;references&quot;&gt;References&lt;/h3&gt;

&lt;p&gt;There’s plenty of other reading material out there on ChatOps, consider checking these out:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=F8Vfoz7GeHw&quot;&gt;Jason Hand on a Beginners Guide to ChatOps&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://hodgkins.io/chatops-on-windows-with-hubot-and-powershell&quot;&gt;Matt Hodgkins on ChatOps on Windows with Hubot and PowerShell&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=XIMOFnfdOx0&quot;&gt;Matt Hodgkins on ChatOps with PowerShell&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;what-about-poshbot-itself&quot;&gt;What about PoshBot itself?&lt;/h4&gt;

&lt;p&gt;Poshbot is quite new, compared to bots like Hubot or Errbot - there’s not much out there yet!&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=36fkyKYq43c&quot;&gt;Brandon Olin on PoshBot&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://poshbot.readthedocs.io/en/latest/&quot;&gt;PoshBot Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Brandon is doing an awesome job, but help is always welcome - found a bug?  Want to help with docs?  Have a feature request?  Want to learn Pester and write some tests?  &lt;a href=&quot;https://github.com/poshbotio/PoshBot/issues&quot;&gt;Open an issue&lt;/a&gt; or &lt;a href=&quot;https://github.com/poshbotio/PoshBot/pulls&quot;&gt;a pull request&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;Cheers!&lt;/p&gt;

    &lt;p&gt;&lt;a href=&quot;http://ramblingcookiemonster.github.io/PoshBot/&quot;&gt;Getting Started with PoshBot&lt;/a&gt; was originally published by Warren F at &lt;a href=&quot;http://ramblingcookiemonster.github.io&quot;&gt;Rambling Cookie Monster&lt;/a&gt; on June 25, 2017.&lt;/p&gt;
  </content>
</entry>


<entry>
  <title type="html"><![CDATA[Quick Hit&#58; Community Lightning Demos]]></title>
  <link rel="alternate" type="text/html" href="http://ramblingcookiemonster.github.io/Summit-Lightning-Demos/" />
  <id>http://ramblingcookiemonster.github.io/Summit-Lightning-Demos</id>
  <updated>2017-03-20T07:00:00+00:00</updated>
  <published>2017-03-20T07:00:00+00:00</published>
  
  <author>
    <name>Warren F</name>
    <uri>http://ramblingcookiemonster.github.io</uri>
    
  </author>
  <content type="html">
    &lt;section id=&quot;table-of-contents&quot; class=&quot;toc&quot;&gt;
  &lt;header&gt;
    &lt;h3&gt;&lt;i class=&quot;fa fa-book&quot;&gt;&lt;/i&gt; Overview&lt;/h3&gt;
  &lt;/header&gt;
&lt;div id=&quot;drawer&quot;&gt;
&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#rambling&quot; id=&quot;markdown-toc-rambling&quot;&gt;Rambling&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#lightning-demos&quot; id=&quot;markdown-toc-lightning-demos&quot;&gt;Lightning Demos&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#community-lightning-demos&quot; id=&quot;markdown-toc-community-lightning-demos&quot;&gt;Community Lightning Demos&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#sign-me-up&quot; id=&quot;markdown-toc-sign-me-up&quot;&gt;Sign me up!&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

  &lt;/div&gt;
&lt;/section&gt;
&lt;!-- /#table-of-contents --&gt;

&lt;h2 id=&quot;rambling&quot;&gt;Rambling&lt;/h2&gt;

&lt;p&gt;Picking between traditional sessions at conferences can be a pain.  What if the content, speaker, or topic don’t quite live up to your expectations?  Now you get to sit through the rest, or try to awkwardly sneak out and catch only a portion of another session.&lt;/p&gt;

&lt;p&gt;On the other side of the fence, some folks might be scared off from speaking at a conference.  45+ minutes of material can take time to come up with.  Demo gods require sacrifices.  A full session can seem overwhelming, particularly if this is your first time presenting.&lt;/p&gt;

&lt;p&gt;A fun non-traditional option is to do a round of lightning demos.  Thankfully, the folks at the &lt;a href=&quot;https://powershell.org/summit/&quot;&gt;2017 PowerShell + DevOps Summit&lt;/a&gt; accepted a proposal for community lightning demos, which I’ll be honored to host!&lt;/p&gt;

&lt;h2 id=&quot;lightning-demos&quot;&gt;Lightning Demos&lt;/h2&gt;

&lt;p&gt;For the past few years, the PowerShell team lightning demos have been a consistent highlight of the NA PowerShell Summit.  It’s a fast-paced session where team members plug in a laptop, and walk through a quick demo of something cool they’re working on.&lt;/p&gt;

&lt;p&gt;This is a great format:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;As an attendee, you get a variety of content, speakers, and topics; even if one or two bits bomb, chances are you’ll have some awesome takeaways&lt;/li&gt;
  &lt;li&gt;As a presenter, you only need to come up with 5-10 minutes of content; you might not be quite so overwhelmed if you’re only up there for a few minutes, and it can be comforting knowing that a number of your peers will be joining you&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So!  Given how mainstream and important open source projects are becoming, a round of community lightning demos could be quite fun and enlightening.  What should you show?&lt;/p&gt;

&lt;h3 id=&quot;community-lightning-demos&quot;&gt;Community Lightning Demos&lt;/h3&gt;

&lt;p&gt;This is all up to you, but here’s a basic recipe that should be easy to follow, and help the audience fill in the bits they need to know:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Pick a topic.  Demo a module or function that you wrote, or that you use often.  Describe a helpful tip or trick that you think is worth sharing with the audience.  This is up to you!&lt;/li&gt;
  &lt;li&gt;Put together a quick demo under 10 minutes.  Don’t try to stretch things to hit 10 minutes; if you say what you need in 5 minutes, even better.  If things don’t go as planned and you end up hitting the time limit, folks can catch you after&lt;/li&gt;
  &lt;li&gt;Include info the audience will find helpful:
    &lt;ul&gt;
      &lt;li&gt;What are you talking about?&lt;/li&gt;
      &lt;li&gt;How does it work?  This is a demo after all!&lt;/li&gt;
      &lt;li&gt;Why might the audience use this?&lt;/li&gt;
      &lt;li&gt;Where can they find more?&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s it!&lt;/p&gt;

&lt;p&gt;Here’s a quick example lightning demo walking through PSDepend basics.  Sorry about the volume, there was a sleeping toddler in the other room…&lt;/p&gt;

&lt;iframe width=&quot;640&quot; height=&quot;360&quot; src=&quot;https://www.youtube-nocookie.com/embed/50Z6vEHVgDg?controls=0&amp;amp;showinfo=0&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;

&lt;h3 id=&quot;sign-me-up&quot;&gt;Sign me up!&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://goo.gl/forms/8sCiEeehOiyT2ynh2&quot;&gt;You can sign up here&lt;/a&gt; - more details at the link.&lt;/p&gt;

&lt;p&gt;No need to spend too much time prepping for a few minutes, but do consider:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Putting together a demo.  Mostly code and working examples&lt;/li&gt;
  &lt;li&gt;Running through your demo, including what you think you’ll say&lt;/li&gt;
  &lt;li&gt;Testing your demo again, if you update PowerShell, modules you use, etc.&lt;/li&gt;
  &lt;li&gt;Thinking of the audience.  Can they read your editor?  Is there syntax highlighting?  Can you split lines up on pipes and other natural spots to avoid scrolling horizontally when your editor is zoomed in a bit?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s pretty much it, I’ll be looking forward to some awesome demos!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;a href=&quot;http://muppet.wikia.com/wiki/Vaudeville?file=Tms406-kermit.jpg&quot;&gt;Thumbnail image source&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

    &lt;p&gt;&lt;a href=&quot;http://ramblingcookiemonster.github.io/Summit-Lightning-Demos/&quot;&gt;Quick Hit&amp;#58; Community Lightning Demos&lt;/a&gt; was originally published by Warren F at &lt;a href=&quot;http://ramblingcookiemonster.github.io&quot;&gt;Rambling Cookie Monster&lt;/a&gt; on March 20, 2017.&lt;/p&gt;
  </content>
</entry>


<entry>
  <title type="html"><![CDATA[Quick Hit&#58; Fun with Sorting]]></title>
  <link rel="alternate" type="text/html" href="http://ramblingcookiemonster.github.io/Fun-With-Sorting/" />
  <id>http://ramblingcookiemonster.github.io/Fun-With-Sorting</id>
  <updated>2017-02-24T07:00:00+00:00</updated>
  <published>2017-02-24T07:00:00+00:00</published>
  
  <author>
    <name>Warren F</name>
    <uri>http://ramblingcookiemonster.github.io</uri>
    
  </author>
  <content type="html">
    &lt;section id=&quot;table-of-contents&quot; class=&quot;toc&quot;&gt;
  &lt;header&gt;
    &lt;h3&gt;&lt;i class=&quot;fa fa-book&quot;&gt;&lt;/i&gt; Overview&lt;/h3&gt;
  &lt;/header&gt;
&lt;div id=&quot;drawer&quot;&gt;
&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#rambling&quot; id=&quot;markdown-toc-rambling&quot;&gt;Rambling&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#sorting-urls&quot; id=&quot;markdown-toc-sorting-urls&quot;&gt;Sorting URLs&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#custom-sorting&quot; id=&quot;markdown-toc-custom-sorting&quot;&gt;Custom Sorting&lt;/a&gt;        &lt;ul&gt;
          &lt;li&gt;&lt;a href=&quot;#calculated-properties&quot; id=&quot;markdown-toc-calculated-properties&quot;&gt;Calculated Properties&lt;/a&gt;&lt;/li&gt;
          &lt;li&gt;&lt;a href=&quot;#custom-sorting-1&quot; id=&quot;markdown-toc-custom-sorting-1&quot;&gt;Custom sorting&lt;/a&gt;&lt;/li&gt;
          &lt;li&gt;&lt;a href=&quot;#refactoring&quot; id=&quot;markdown-toc-refactoring&quot;&gt;Refactoring&lt;/a&gt;&lt;/li&gt;
          &lt;li&gt;&lt;a href=&quot;#other-sorting-options&quot; id=&quot;markdown-toc-other-sorting-options&quot;&gt;Other sorting options&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

  &lt;/div&gt;
&lt;/section&gt;
&lt;!-- /#table-of-contents --&gt;

&lt;h2 id=&quot;rambling&quot;&gt;Rambling&lt;/h2&gt;

&lt;p&gt;So!  I’m updating a squid file to remove some duplicate URLs that break things, when my mild OCD gets the better of me.  These damn URLs are not in order!&lt;/p&gt;

&lt;p&gt;Rather than resorting to a few GNU tools, I realized I could use &lt;a href=&quot;https://github.com/powershell/powershell&quot;&gt;PowerShell from my Mac&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;sorting-urls&quot;&gt;Sorting URLs&lt;/h2&gt;

&lt;p&gt;We’ll use the following URLs in our examples:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$URLs&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'dept55.host.tld'&lt;/span&gt;,
        &lt;span class=&quot;s1&quot;&gt;'dept1.host.tld'&lt;/span&gt;,
        &lt;span class=&quot;s1&quot;&gt;'some-other-host.net'&lt;/span&gt;,
        &lt;span class=&quot;s1&quot;&gt;'a.dept55.host.tld'&lt;/span&gt;,
        &lt;span class=&quot;s1&quot;&gt;'first.com'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We’ll try the obvious &lt;code class=&quot;highlighter-rouge&quot;&gt;Sort-Object&lt;/code&gt; first:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$urls&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;Sort-Object&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# a.dept55.host.tld&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# dept1.host.tld&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# dept55.host.tld&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# first.com&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# some-other-host.net&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Nope nope nope.  I want my dept55 subdomains grouped together.  Also, first.com should come before anything host.tld.&lt;/p&gt;

&lt;p&gt;It looks like we need to sort by pieces of this string.  How do we do that?&lt;/p&gt;

&lt;h3 id=&quot;custom-sorting&quot;&gt;Custom Sorting&lt;/h3&gt;

&lt;h4 id=&quot;calculated-properties&quot;&gt;Calculated Properties&lt;/h4&gt;

&lt;p&gt;You might be familiar with calculated properties.  The syntax is ugly, but they’re incredibly helpful, and we can always use snippets!&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;New-IseSnippet&lt;/span&gt; -Title &lt;span class=&quot;s2&quot;&gt;&quot;Calculated Property&quot;&lt;/span&gt; -description &lt;span class=&quot;s2&quot;&gt;&quot;Create a calculated property&quot;&lt;/span&gt; -text &lt;span class=&quot;s1&quot;&gt;'@{ label = &quot;&quot;; expression = {} }'&lt;/span&gt; -Author Blah -CaretOffset 12 -ErrorAction SilentlyContinue -force
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Shorthand, we can just use &lt;code class=&quot;highlighter-rouge&quot;&gt;@{ l=@{}; e=@{} }&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That’s a bit ugly, but let’s show what this can do:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$Example&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;pscustomobject]@&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    One &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; 1
    Two &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; 2
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;$Example&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# One Two&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# --- ---&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#   1   2&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;$Example&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;Select-Object&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;, @&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;l&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'Five'&lt;/span&gt;;&lt;span class=&quot;nv&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;={&lt;/span&gt;5&lt;span class=&quot;o&quot;&gt;}}&lt;/span&gt;, @&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;l&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'Multiple'&lt;/span&gt;;&lt;span class=&quot;nv&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;={&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$_&lt;/span&gt;.Two &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; 2&lt;span class=&quot;o&quot;&gt;}}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# One Two Five Multiple&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# --- --- ---- --------&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#  1   2    5        4&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Pretty cool!  We can add properties to an object - they can be static (&lt;code class=&quot;highlighter-rouge&quot;&gt;Five&lt;/code&gt;), or can use logic along with other properties of the object (&lt;code class=&quot;highlighter-rouge&quot;&gt;Multiple&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Do keep in mind this kills the fidelity of the object.  You get the properties, but none of the methods on the resulting selected object.  If you need to keep your object pristine, you can use &lt;code class=&quot;highlighter-rouge&quot;&gt;Add-Member&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;Add-Member&lt;/span&gt; -InputObject &lt;span class=&quot;nv&quot;&gt;$Example&lt;/span&gt; -Type NoteProperty -Name Five -Value 5
&lt;span class=&quot;nb&quot;&gt;Add-Member&lt;/span&gt; -InputObject &lt;span class=&quot;nv&quot;&gt;$Example&lt;/span&gt; -Type NoteProperty -Name Multiple -Value &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$Example&lt;/span&gt;.Two &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; 2&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;$Example&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# One Two Five Multiple&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# --- --- ---- --------&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#  1   2    5        4&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Wait… weren’t we talking about sorting?&lt;/p&gt;

&lt;h4 id=&quot;custom-sorting-1&quot;&gt;Custom sorting&lt;/h4&gt;

&lt;p&gt;So!  With a calculated property, we provided a name and expression.  When sorting, all we need is the expression scriptblock:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$Urls&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;Sort-Object&lt;/span&gt; @&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;={&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$_&lt;/span&gt; -split &lt;span class=&quot;s1&quot;&gt;'\.'&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)[&lt;/span&gt;-2] &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# first.com&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# a.dept55.host.tld&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# dept55.host.tld&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# dept1.host.tld&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# some-other-host.net&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That’s a start!  What happened?  We basically said to sort by…&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Splitting the items on &lt;code class=&quot;highlighter-rouge&quot;&gt;.&lt;/code&gt;.  Split uses regex, so we escape the &lt;code class=&quot;highlighter-rouge&quot;&gt;.&lt;/code&gt;, hence &lt;code class=&quot;highlighter-rouge&quot;&gt;\.&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Picking the second to last item in the array (&lt;code class=&quot;highlighter-rouge&quot;&gt;[-2]&lt;/code&gt;): we don’t want to sort on TLDs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Hmm.  You might have noticed that even though host.tld is sorted behind first.com, dept55 is in front of dept1.  That doesn’t look right!&lt;/p&gt;

&lt;p&gt;So, we need to sort on several components, not just the host name:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$Urls&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;Sort-Object&lt;/span&gt; @&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;={&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$_&lt;/span&gt; -split &lt;span class=&quot;s1&quot;&gt;'\.'&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)[&lt;/span&gt;-2] &lt;span class=&quot;o&quot;&gt;}}&lt;/span&gt;, @&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;={&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$_&lt;/span&gt; -split &lt;span class=&quot;s1&quot;&gt;'\.'&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)[&lt;/span&gt;-3] &lt;span class=&quot;o&quot;&gt;}}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# first.com&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# dept1.host.tld&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# dept55.host.tld&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# a.dept55.host.tld&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# some-other-host.net&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We’re on the way!  We sort by the second to last item, and then third to last item.&lt;/p&gt;

&lt;h4 id=&quot;refactoring&quot;&gt;Refactoring&lt;/h4&gt;

&lt;p&gt;Normally, I would use the code above.  It’s more readable, even if it repeats code.  That said, let’s illustrate a re-usable means to handle some sorting scenarios:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Go a few layers deep&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$URLs&lt;/span&gt; | &lt;span class=&quot;k&quot;&gt;Foreach&lt;/span&gt;-Object &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$Array&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$_&lt;/span&gt; -split &lt;span class=&quot;s1&quot;&gt;'\.'&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;pscustomobject]@&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        Item &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$_&lt;/span&gt;
        2 &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$Array&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;-2]
        3 &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$Array&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;-3]
        4 &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$Array&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;-4]
        5 &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$Array&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;-5]
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;Sort-Object&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$_&lt;/span&gt;.2&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;, &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$_&lt;/span&gt;.3&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;, &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$_&lt;/span&gt;.4&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;, &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$_&lt;/span&gt;.5&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; |
    &lt;span class=&quot;nb&quot;&gt;Select&lt;/span&gt; -ExpandProperty Item

&lt;span class=&quot;c1&quot;&gt;# first.com&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# dept1.host.tld&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# dept55.host.tld&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# a.dept55.host.tld&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# some-other-host.net&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Notice we didn’t use the &lt;code class=&quot;highlighter-rouge&quot;&gt;@{e={}}&lt;/code&gt; syntax here. Turns out this is unnecessary, all we need is a scriptblock!&lt;/p&gt;

&lt;p&gt;What happened here?&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;We break the URL into an array of strings&lt;/li&gt;
  &lt;li&gt;We create a new object that has the original item to sort, along with pieces of the array to sort on&lt;/li&gt;
  &lt;li&gt;We sort on the pieces of the array&lt;/li&gt;
  &lt;li&gt;We extract the item we wanted to sort&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So!  Even if this isn’t the best code to use for this particular situation, do keep the gist of this in mind, it can come in handy:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Create an object with the item to sort, and properties to sort on.  Those properties might involve further logic and query results.&lt;/li&gt;
  &lt;li&gt;Sort on the properties in question&lt;/li&gt;
  &lt;li&gt;Extract the item you’re sorting&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;other-sorting-options&quot;&gt;Other sorting options&lt;/h4&gt;

&lt;p&gt;As with any language, there are many ways to accomplish a task.  Some folks will assemble a string that helps them sort.  Others will use type coercion and native sorting for those types (&lt;a href=&quot;http://www.madwithpowershell.com/2016/03/sorting-ip-addresses-in-powershell-part.html&quot;&gt;cool example from Tim Curwick&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;While all this may seem complicated, be happy that we don’t need to write our own &lt;a href=&quot;https://www.toptal.com/developers/sorting-algorithms&quot;&gt;sorting algorithms&lt;/a&gt;!  Unless you’re a masochist that is.&lt;/p&gt;

    &lt;p&gt;&lt;a href=&quot;http://ramblingcookiemonster.github.io/Fun-With-Sorting/&quot;&gt;Quick Hit&amp;#58; Fun with Sorting&lt;/a&gt; was originally published by Warren F at &lt;a href=&quot;http://ramblingcookiemonster.github.io&quot;&gt;Rambling Cookie Monster&lt;/a&gt; on February 24, 2017.&lt;/p&gt;
  </content>
</entry>


<entry>
  <title type="html"><![CDATA[PSDepend: PowerShell Dependencies]]></title>
  <link rel="alternate" type="text/html" href="http://ramblingcookiemonster.github.io/PSDepend/" />
  <id>http://ramblingcookiemonster.github.io/PSDepend</id>
  <updated>2017-01-06T07:00:00+00:00</updated>
  <published>2017-01-06T07:00:00+00:00</published>
  
  <author>
    <name>Warren F</name>
    <uri>http://ramblingcookiemonster.github.io</uri>
    
  </author>
  <content type="html">
    &lt;section id=&quot;table-of-contents&quot; class=&quot;toc&quot;&gt;
  &lt;header&gt;
    &lt;h3&gt;&lt;i class=&quot;fa fa-book&quot;&gt;&lt;/i&gt; Overview&lt;/h3&gt;
  &lt;/header&gt;
&lt;div id=&quot;drawer&quot;&gt;
&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#rambling&quot; id=&quot;markdown-toc-rambling&quot;&gt;Rambling&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#psdepend&quot; id=&quot;markdown-toc-psdepend&quot;&gt;PSDepend&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#whats-a-requirementspsd1&quot; id=&quot;markdown-toc-whats-a-requirementspsd1&quot;&gt;What’s a requirements.psd1?&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#why-psdepend&quot; id=&quot;markdown-toc-why-psdepend&quot;&gt;Why PSDepend?&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#what-dependencies-does-psdepend-support&quot; id=&quot;markdown-toc-what-dependencies-does-psdepend-support&quot;&gt;What dependencies does PSDepend support?&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#that-syntax-seems-too-simplistic&quot; id=&quot;markdown-toc-that-syntax-seems-too-simplistic&quot;&gt;That syntax seems too simplistic&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#getting-started-with-psdepend&quot; id=&quot;markdown-toc-getting-started-with-psdepend&quot;&gt;Getting Started with PSDepend&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#what-commands-can-i-run&quot; id=&quot;markdown-toc-what-commands-can-i-run&quot;&gt;What commands can I run?&lt;/a&gt;        &lt;ul&gt;
          &lt;li&gt;&lt;a href=&quot;#get-psdependtype&quot; id=&quot;markdown-toc-get-psdependtype&quot;&gt;Get-PSDependType&lt;/a&gt;&lt;/li&gt;
          &lt;li&gt;&lt;a href=&quot;#get-dependency&quot; id=&quot;markdown-toc-get-dependency&quot;&gt;Get-Dependency&lt;/a&gt;&lt;/li&gt;
          &lt;li&gt;&lt;a href=&quot;#invoke-psdepend&quot; id=&quot;markdown-toc-invoke-psdepend&quot;&gt;Invoke-PSDepend&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#example-module-based-virtual-environments&quot; id=&quot;markdown-toc-example-module-based-virtual-environments&quot;&gt;Example: Module Based Virtual Environments&lt;/a&gt;        &lt;ul&gt;
          &lt;li&gt;&lt;a href=&quot;#demo&quot; id=&quot;markdown-toc-demo&quot;&gt;Demo&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#next-steps&quot; id=&quot;markdown-toc-next-steps&quot;&gt;Next Steps&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

  &lt;/div&gt;
&lt;/section&gt;
&lt;!-- /#table-of-contents --&gt;

&lt;h2 id=&quot;rambling&quot;&gt;Rambling&lt;/h2&gt;

&lt;p&gt;The first weeks of August 2016 were great.  I spent most of my time relaxing on the porch, with some sailing, swimming, and ice cream mixed in.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/psdepend/cape-big.jpg&quot;&gt;&lt;img src=&quot;/images/psdepend/cape.jpg&quot; alt=&quot;cape&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Of course, I couldn’t help myself, and spent a little time on a fun side project: &lt;a href=&quot;https://github.com/RamblingCookieMonster/PSDepend&quot;&gt;PSDepend&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;psdepend&quot;&gt;PSDepend&lt;/h2&gt;

&lt;p&gt;What is this PSDepend thing?  Long story short, it’s a way for you to tell PowerShell that you need certain PowerShell modules, git repositories, and other dependencies, using a small requirements.psd1 file.&lt;/p&gt;

&lt;h3 id=&quot;whats-a-requirementspsd1&quot;&gt;What’s a requirements.psd1?&lt;/h3&gt;

&lt;p&gt;Here’s a quick example to illustrate the basics of a &lt;code class=&quot;highlighter-rouge&quot;&gt;requirements.psd1&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;@&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    psake        &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'latest'&lt;/span&gt;
    Pester       &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'latest'&lt;/span&gt;
    BuildHelpers &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'0.0.20'&lt;/span&gt;
    PSDeploy     &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'0.1.21'&lt;/span&gt;

    &lt;span class=&quot;s1&quot;&gt;'RamblingCookieMonster/PowerShell'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'master'&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is pretty straightforward: from the PowerShell Gallery, we want the latest Pester and psake, and specific versions of BuildHelpers and PSDeploy.  We also want to download a GitHub repo’s master branch.&lt;/p&gt;

&lt;p&gt;I can now run &lt;code class=&quot;highlighter-rouge&quot;&gt;Invoke-PSDepend&lt;/code&gt; against this requirements file, and pull in my dependencies.&lt;/p&gt;

&lt;p&gt;Before we dive in, why would we want something like this?&lt;/p&gt;

&lt;h3 id=&quot;why-psdepend&quot;&gt;Why PSDepend?&lt;/h3&gt;

&lt;p&gt;You can probably come up with more creative scenarios, but here are a few use cases that came to mind:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Provide functional dependency documentation for a project&lt;/li&gt;
  &lt;li&gt;Enable something like a module-focused virtual environment for PowerShell&lt;/li&gt;
  &lt;li&gt;Simplify sharing code with non-PowerShell-savvy folks&lt;/li&gt;
  &lt;li&gt;Help ensure a consistent runtime environment and improve portability for PowerShell solutions&lt;/li&gt;
  &lt;li&gt;Quickly set up a fresh dev/test/etc. PowerShell environments&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;How many times have you had to describe the steps to resolve prerequisites for a function, module, script, or other PowerShell code?  Did you have to start from ground zero and explain what a module is and how to install it?&lt;/p&gt;

&lt;p&gt;It turns out, many other languages have solved this problem.  In Python, you might &lt;code class=&quot;highlighter-rouge&quot;&gt;pip install -r requirements.txt&lt;/code&gt;.  In Ruby, you might &lt;code class=&quot;highlighter-rouge&quot;&gt;bundle install&lt;/code&gt;.  In PowerShell? You hard code solution-specific logic into your code, abstract this out into configuration management, or just manually install the prerequisites and hope you don’t forget about them if you re-deploy your system.&lt;/p&gt;

&lt;p&gt;Shouldn’t something like this already exist?  Michael Willis has already started on the awesome looking &lt;a href=&quot;https://github.com/xainey/psrequire&quot;&gt;PSRequire&lt;/a&gt; - unfortunately, I still need to support PowerShell pre-v5, so the class-based PSRequire wasn’t for me; time for a side project!&lt;/p&gt;

&lt;h3 id=&quot;what-dependencies-does-psdepend-support&quot;&gt;What dependencies does PSDepend support?&lt;/h3&gt;

&lt;p&gt;At the time of writing, we support a few dependency types:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;PSGalleryModule&lt;/strong&gt;: Modules from the PowerShell Gallery, via PowerShellGet&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Package&lt;/strong&gt;: Install a package using the PackageManagement module&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;PSGalleryNuget&lt;/strong&gt;: Modules from the PowerShell Gallery, without the need of PowerShellGet&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Git&lt;/strong&gt;: Clone a repo and checkout a branch/commit/tag&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;GitHub&lt;/strong&gt;: Download a specific branch or commit from a repo on GitHub, and extract a PowerShell module if we find it (Thanks to Doug Finke for some &lt;a href=&quot;https://github.com/dfinke/InstallModuleFromGitHub&quot;&gt;starter code&lt;/a&gt;!)&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;FileDownload&lt;/strong&gt;: Download a file&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;FileSystem&lt;/strong&gt;: Copy a file or folder&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Task&lt;/strong&gt;: Run a pre-defined PowerShell script&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Command&lt;/strong&gt;: Run a PowerShell command&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;More to come - Nuget for example - but we’d love ideas or pull requests!  You can always run &lt;code class=&quot;highlighter-rouge&quot;&gt;Get-PSDependType&lt;/code&gt; to view available dependency types.&lt;/p&gt;

&lt;h3 id=&quot;that-syntax-seems-too-simplistic&quot;&gt;That syntax seems too simplistic&lt;/h3&gt;

&lt;p&gt;It probably is!  That being said, the simple syntax we showed with a &lt;code class=&quot;highlighter-rouge&quot;&gt;ModuleName = 'Version'&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;’Account/Repo’ = 'Version'&lt;/code&gt; is just a shortcut.  We also support a more flexible syntax:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;@&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    PSDeploy_0_1_21 &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; @&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        DependencyType &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'PSGalleryNuget'&lt;/span&gt;
        Name &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'PSDeploy'&lt;/span&gt;
        Version &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'0.1.21'&lt;/span&gt;
        Target &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;C:\ProjectX&quot;&lt;/span&gt;
        Tags &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'prod'&lt;/span&gt;
        DependsOn &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'BuildHelpers'&lt;/span&gt;
        AddToPath &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$True&lt;/span&gt;
        PostScripts &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'C:\SomeScripts.ps1'&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# You can still mix in simple syntax&lt;/span&gt;
    BuildHelpers &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'0.0.20'&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Whew! That was a bit convoluted. What does it actually do?&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;We make sure to install the BuildHelpers dependency first (&lt;code class=&quot;highlighter-rouge&quot;&gt;DependsOn&lt;/code&gt;)&lt;/li&gt;
  &lt;li&gt;We download PSDeploy, version 0.1.21, from the PowerShell Gallery (&lt;code class=&quot;highlighter-rouge&quot;&gt;Name&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;Version&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;DependencyType&lt;/code&gt;)&lt;/li&gt;
  &lt;li&gt;We use a nuget.exe (and grab it for you if you don’t have it), rather than depend on PowerShellGet (PSGalleryNuget &lt;code class=&quot;highlighter-rouge&quot;&gt;DependencyType&lt;/code&gt;)&lt;/li&gt;
  &lt;li&gt;We install this to &lt;code class=&quot;highlighter-rouge&quot;&gt;C:\ProjectX\PSDeploy&lt;/code&gt; (&lt;code class=&quot;highlighter-rouge&quot;&gt;Target&lt;/code&gt;)&lt;/li&gt;
  &lt;li&gt;We only run the PSDepend install if &lt;code class=&quot;highlighter-rouge&quot;&gt;Invoke-PSDepend&lt;/code&gt; is called with &lt;code class=&quot;highlighter-rouge&quot;&gt;-Tags prod&lt;/code&gt; (&lt;code class=&quot;highlighter-rouge&quot;&gt;Tags&lt;/code&gt;)&lt;/li&gt;
  &lt;li&gt;We add &lt;code class=&quot;highlighter-rouge&quot;&gt;C:\ProjectX&lt;/code&gt; (&lt;code class=&quot;highlighter-rouge&quot;&gt;Target&lt;/code&gt;) to $ENV:PSModulePath (&lt;code class=&quot;highlighter-rouge&quot;&gt;AddToPath&lt;/code&gt;)&lt;/li&gt;
  &lt;li&gt;We run &lt;code class=&quot;highlighter-rouge&quot;&gt;C:\SomeScripts.ps1&lt;/code&gt; after we’ve downloaded PSDeploy (&lt;code class=&quot;highlighter-rouge&quot;&gt;PostScripts&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can run &lt;code class=&quot;highlighter-rouge&quot;&gt;Get-Help Get-Dependency&lt;/code&gt;, or &lt;code class=&quot;highlighter-rouge&quot;&gt;Get-Help about_PSDepend_Definitions&lt;/code&gt; for details on standard attributes, and &lt;code class=&quot;highlighter-rouge&quot;&gt;Get-PSDependType SomeTypeName -ShowHelp&lt;/code&gt; to see the help for a specific dependency type.&lt;/p&gt;

&lt;p&gt;Let’s dive in and start using PSDepend.&lt;/p&gt;

&lt;h2 id=&quot;getting-started-with-psdepend&quot;&gt;Getting Started with PSDepend&lt;/h2&gt;

&lt;p&gt;Getting up and running with PSDepend is simple!&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# PowerShell 5 or PowerShellGet installed:&lt;/span&gt;
Install-Module PSDepend

&lt;span class=&quot;c1&quot;&gt;# PowerShell 3 or 4, curl|bash bootstrap. Read before running something like this...&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;Invoke-Expression&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;New-Object &lt;/span&gt;System.Net.WebClient&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;.DownloadString&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'https://raw.github.com/ramblingcookiemonster/PSDepend/Examples/Install-PSDepend.ps1'&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Git&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Download the repository&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Unblock the zip&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Extract the PSDepend folder to a module path (e.g. $env:USERPROFILE\Documents\WindowsPowerShell\Modules\)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Import and start exploring&lt;/span&gt;
Import-Module PSDepend
Get-Command -Module PSDepend
Get-Help about_PSDepend
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That’s it!  Keep in mind we bootstrap nuget.exe when you import the module for the first time, unless you have it in your path or adjust &lt;a href=&quot;https://github.com/RamblingCookieMonster/PSDepend/blob/master/PSDepend/PSDepend.Config&quot;&gt;the config file&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Now that you have PSDepend, we can start kicking the tires.&lt;/p&gt;

&lt;h3 id=&quot;what-commands-can-i-run&quot;&gt;What commands can I run?&lt;/h3&gt;

&lt;p&gt;PSDepend includes commands to do things, and to help explore PSDepend:&lt;/p&gt;

&lt;h4 id=&quot;get-psdependtype&quot;&gt;Get-PSDependType&lt;/h4&gt;

&lt;p&gt;This is a quick way to see what dependency types are available:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Get-PSDependType
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;DependencyType  Description               DependencyScript
--------------  -----------               ----------------
FileDownload    Download a file           C:\...PSDependScripts\FileDownload.ps1
FileSystem      Copy a file or folder     C:\...PSDependScripts\FileSystem.ps1
Git             Clone a git repository    C:\...PSDependScripts\Git.ps1
PSGalleryModule Install a PowerShell m... C:\...PSDependScripts\PSGalleryModule.ps1
PSGalleryNuget  Install a PowerShell m... C:\...PSDependScripts\PSGalleryNuget.ps1
Task            Support dependencies b... C:\...PSDependScripts\Task.ps1
...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You can also use it to get help information for one of these types:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Get-PSDependType PSGalleryModule -ShowHelp
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;...
SYNOPSIS
    Installs a module from a PowerShell repository like the PowerShell Gallery.

SYNTAX
    C:\Users\wframe\Documents\GitHub\PSDepend\PSDepend\PSDependScripts\PSGalleryModule.ps1 [[-Dependency] &amp;lt;PSObject[]&amp;gt;] [[-Repository]
    &amp;lt;String&amp;gt;] [-Force] [-Import] [&amp;lt;CommonParameters&amp;gt;]

DESCRIPTION
    Installs a module from a PowerShell repository like the PowerShell Gallery.

    Relevant Dependency metadata:
        Name: The name for this module
        Version: Used to identify existing installs meeting this criteria, and as RequiredVersion for installation.  Defaults to 'latest'
        Target: Used as 'Scope' for Install-Module.  If this is a path, we use Save-Module with this path.  Defaults to 'AllUsers'
        AddToPath: If target is used as a path, prepend that path to ENV:PSModulePath
...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;What else can we do?&lt;/p&gt;

&lt;h4 id=&quot;get-dependency&quot;&gt;Get-Dependency&lt;/h4&gt;

&lt;p&gt;If you want to parse a requirements.psd1 file, you can run &lt;code class=&quot;highlighter-rouge&quot;&gt;Get-Dependency&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;By default this will look for a requirements.psd1 or a *.depend.psd1 file in the current path.  You can also use &lt;code class=&quot;highlighter-rouge&quot;&gt;-Recurse&lt;/code&gt;, or specify a &lt;code class=&quot;highlighter-rouge&quot;&gt;-Path&lt;/code&gt;.  Here’s the output from our flexible psd1 example above:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Get-Dependency | &lt;span class=&quot;nb&quot;&gt;Select-Object&lt;/span&gt; -Property &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;DependencyFile  : C:\temp\requirements.psd1
DependencyName  : BuildHelpers
DependencyType  : PSGalleryModule
Name            : BuildHelpers
Version         : 0.0.20
Parameters      :
Source          :
Target          :
AddToPath       :
Tags            :
DependsOn       :
PreScripts      :
PostScripts     :
PSDependOptions :
Raw             :

DependencyFile  : C:\temp\requirements.psd1
DependencyName  : PSDeploy_0_1_21
DependencyType  : PSGalleryNuget
Name            : PSDeploy
Version         : 0.1.21
Parameters      :
Source          :
Target          : C:\ProjectX
AddToPath       : True
Tags            : prod
DependsOn       : BuildHelpers
PreScripts      :
PostScripts     : C:\SomeScripts.ps1
PSDependOptions :
Raw             : {Tags, Version, AddToPath...}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Notice that BuildHelpers is listed first (PSDeploy_0_1_21 DependsOn it).  You can use &lt;code class=&quot;highlighter-rouge&quot;&gt;Get-Dependency&lt;/code&gt; to verify that PSDepend will read your requirements.psd1 file as expected.&lt;/p&gt;

&lt;p&gt;So!  We know how to read a requirements file, but how do we actually install these dependencies?&lt;/p&gt;

&lt;h4 id=&quot;invoke-psdepend&quot;&gt;Invoke-PSDepend&lt;/h4&gt;

&lt;p&gt;Invoke-PSDepend can kick off a test, install, or import of a dependency.  By default, it will search recursively for any requirements.psd1 file, or *.depend.psd1 file under the current path, similar to &lt;code class=&quot;highlighter-rouge&quot;&gt;Invoke-Pester&lt;/code&gt; or &lt;code class=&quot;highlighter-rouge&quot;&gt;Invoke-PSDeploy&lt;/code&gt;, and run the install action.&lt;/p&gt;

&lt;p&gt;Using the same requirements.psd1, let’s verify that our dependencies aren’t already in place:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Invoke-PSDepend C:\temp\requirements.psd1 -Test |
    &lt;span class=&quot;nb&quot;&gt;Select-Object&lt;/span&gt; -Property Dependency&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;DependencyFile            DependencyName  DependencyType  DependencyExists
--------------            --------------  --------------  ----------------
C:\temp\requirements.psd1 BuildHelpers    PSGalleryModule            False
C:\temp\requirements.psd1 PSDeploy_0_1_21 PSGalleryNuget             False
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Invoke-PSDepend C:\temp\requirements.psd1 -Test -Quiet
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;False
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now, let’s kick off PSDepend:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Invoke-PSDepend -Path C:\temp
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After a couple seconds, our dependencies are in place:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Invoke-PSDepend C:\temp\requirements.psd1 -Test –Quiet
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;True
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We can verify that the Target for PSDeploy was added to the PSModulePath:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ENV&lt;/span&gt;:PSModulePath
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;C:\ProjectX;C:\Users\wframe...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Perhaps we want to import these dependencies, and validate that they imported:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Invoke-PSDepend C:\temp\requirements.psd1 -Import
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Get-Module -Name BuildHelpers, PSDeploy |
    &lt;span class=&quot;nb&quot;&gt;Select-Object&lt;/span&gt; -Property Version, Path
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Version Path
------- ----
0.0.20  C:\Program Files\WindowsPowerShell\Modules\BuildHelpers\0.0.20\BuildHelpers.psm1
0.1.21  C:\ProjectX\PSDeploy\PSDeploy.psm1

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

&lt;p&gt;Enough blather, let’s cover a practical scenario where this might come in handy!&lt;/p&gt;

&lt;h3 id=&quot;example-module-based-virtual-environments&quot;&gt;Example: Module Based Virtual Environments&lt;/h3&gt;

&lt;p&gt;PSDepend can help create minimalistic virtual environments in a few ways:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Prepending &lt;code class=&quot;highlighter-rouge&quot;&gt;$ENV:Path&lt;/code&gt; with one or more paths&lt;/li&gt;
  &lt;li&gt;Prepending &lt;code class=&quot;highlighter-rouge&quot;&gt;$ENV:PSModulePath&lt;/code&gt; with one or more paths&lt;/li&gt;
  &lt;li&gt;Importing modules&lt;/li&gt;
  &lt;li&gt;Installing modules and files in deterministic locations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s pretend we’re working on ProjectX.  Our systems may have different versions of modules, perhaps due to conflicting requirements from other projects.  This is a common theme with languages like Python and Ruby, and will likely become more of an issue in PowerShell.&lt;/p&gt;

&lt;p&gt;Do we just replace those modules and break things?  Do we need to include our own custom code for dependencies with each of our projects, perhaps using PowerShell 5’s side-by-side versioning?  No thank you, I’ll try this with PSDepend!&lt;/p&gt;

&lt;h4 id=&quot;demo&quot;&gt;Demo&lt;/h4&gt;

&lt;p&gt;Once we’ve identified our dependencies, we build a requirements.psd1 file:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;@&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Set some global, override-able defaults&lt;/span&gt;
    PSDependOptions &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; @&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        Target &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'C:\ProjectX'&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# Grab some modules&lt;/span&gt;
    PSSlack &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'0.0.15'&lt;/span&gt;
    ImportExcel &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'2.2.7'&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;'Posh-SSH'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'latest'&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# Download a GitHub repo&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;'ramblingcookiemonster/PowerShell'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'master'&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# Grab an internal module&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;'PSAMS'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; @&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        DependencyType &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'FileSystem'&lt;/span&gt;
        Source &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'\\FileServer01\PowerShell\PSAMS'&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# Download a file&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;'psrabbitmq.dll'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; @&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        DependencyType &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'FileDownload'&lt;/span&gt;
        Source &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'https://github.com/RamblingCookieMonster/PSRabbitMq/raw/master/PSRabbitMq/lib/RabbitMQ.Client.dll'&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I’ll save this in C:\ProjectX\requirements.psd1, and invoke it:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Invoke-PSDepend C:\ProjectX -Force
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We can verify that we have the resulting files:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Invoke-PSDepend C:\ProjectX -Test –Quiet
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;True
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;Get-ChildItem &lt;/span&gt;C:\ProjectX | &lt;span class=&quot;nb&quot;&gt;Select-Object&lt;/span&gt; -Property Name
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Name
----
ImportExcel
Posh-SSH
PowerShell
psams
PSSlack
RabbitMQ.Client.dll
requirements.psd1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Finally, if we need these modules in our project, we can import them with a one liner:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Import dependencies!&lt;/span&gt;
Invoke-PSDepend C:\ProjectX –Import -Force

&lt;span class=&quot;c1&quot;&gt;# Trust, but verify&lt;/span&gt;
Get-Module -Name PSSlack, ImportExcel, Posh-SSH, PSAMS
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ModuleType Version Name        ExportedCommands
---------- ------- ----        ----------------
Script     2.2.7   ImportExcel {Add-WorkSheet, BarChart, Col...
Manifest   1.7.6   Posh-SSH    {Get-SCPFile, Get-SCPFolder, ...
Script     0.0.1   psams       {Get-AmsLab, Get-AmsLabMember...
Script     0.0.15  PSSlack     {Find-SlackMessage, Get-PSSla...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That’s it!  A small requirements.psd1 file, and we now have our modules loaded and available to re-use in &lt;code class=&quot;highlighter-rouge&quot;&gt;C:\ProjectX&lt;/code&gt;, even if our &lt;code class=&quot;highlighter-rouge&quot;&gt;$ENV:PSModulePath&lt;/code&gt; includes these modules at incompatible versions.&lt;/p&gt;

&lt;h2 id=&quot;next-steps&quot;&gt;Next Steps&lt;/h2&gt;

&lt;p&gt;So!  An early version of PSDepend is out there, you can find the raw code &lt;a href=&quot;https://github.com/RamblingCookieMonster/PSDepend&quot;&gt;on GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If this is something you might use, kick the tires a bit!  Feel free to submit an issue for an idea or bug, or a pull request to fix bugs, code smells, documentation, lack of tests, or anything else that you think might help.&lt;/p&gt;

&lt;p&gt;Cheers!&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.fontechiara.com/gastronomy-food-le-marche/italian-food.html&quot;&gt;&lt;em&gt;Thumbnail credit&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;

    &lt;p&gt;&lt;a href=&quot;http://ramblingcookiemonster.github.io/PSDepend/&quot;&gt;PSDepend: PowerShell Dependencies&lt;/a&gt; was originally published by Warren F at &lt;a href=&quot;http://ramblingcookiemonster.github.io&quot;&gt;Rambling Cookie Monster&lt;/a&gt; on January 06, 2017.&lt;/p&gt;
  </content>
</entry>


<entry>
  <title type="html"><![CDATA[Quick Hit&#58; Extract Scheduled Task Credentials]]></title>
  <link rel="alternate" type="text/html" href="http://ramblingcookiemonster.github.io/Quick-Hits-and-Some-Mischief/" />
  <id>http://ramblingcookiemonster.github.io/Quick-Hits-and-Some-Mischief</id>
  <updated>2016-09-09T07:00:00+00:00</updated>
  <published>2016-09-09T07:00:00+00:00</published>
  
  <author>
    <name>Warren F</name>
    <uri>http://ramblingcookiemonster.github.io</uri>
    
  </author>
  <content type="html">
    &lt;section id=&quot;table-of-contents&quot; class=&quot;toc&quot;&gt;
  &lt;header&gt;
    &lt;h3&gt;&lt;i class=&quot;fa fa-book&quot;&gt;&lt;/i&gt; Overview&lt;/h3&gt;
  &lt;/header&gt;
&lt;div id=&quot;drawer&quot;&gt;
&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#rambling&quot; id=&quot;markdown-toc-rambling&quot;&gt;Rambling&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#get-secret&quot; id=&quot;markdown-toc-get-secret&quot;&gt;Get-Secret&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#scheduled-task-credentials&quot; id=&quot;markdown-toc-scheduled-task-credentials&quot;&gt;Scheduled Task Credentials&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#wrapping-up&quot; id=&quot;markdown-toc-wrapping-up&quot;&gt;Wrapping up&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

  &lt;/div&gt;
&lt;/section&gt;
&lt;!-- /#table-of-contents --&gt;

&lt;h2 id=&quot;rambling&quot;&gt;Rambling&lt;/h2&gt;

&lt;p&gt;I enjoy writing.  I tend to try to write more than a handful of paragraphs, and often pair things up with a module or guide of sorts.  That takes a bit longer than writing a quick hit on something fun I’ve learned.&lt;/p&gt;

&lt;p&gt;This &lt;a href=&quot;http://ramblingcookiemonster.github.io/quick/&quot;&gt;&lt;em&gt;Quick Hits&lt;/em&gt;&lt;/a&gt; section will include bits longer than a tweet, and shorter than a typical post.&lt;/p&gt;

&lt;p&gt;Quantity over quality, right? : )&lt;/p&gt;

&lt;h2 id=&quot;get-secret&quot;&gt;Get-Secret&lt;/h2&gt;

&lt;p&gt;So!  At $LastJob, I managed to convince $Boss that password management was somewhat important.&lt;/p&gt;

&lt;p&gt;Among other motivators, I think these stuck out:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Authentication is a thing&lt;/strong&gt;.  With KeePass or a similar solution, &lt;em&gt;anyone&lt;/em&gt; who knows the master password gets in, and can take the safe when they leave&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Authorization is a thing&lt;/strong&gt;.  With KeePass or a similar solution, you’re stuck with multiple archives if you want to share different passwords with different folks.  Which password do you need?  &lt;a href=&quot;https://www.youtube.com/watch?v=MrTsuvykUZk&quot;&gt;Every one&lt;/a&gt;!&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;APIs are a thing&lt;/strong&gt;.  Okay.  KeePass and similar solutions might be able to limp by on this, but an authenticated web API designed for multiple users and the works is a bit nicer.  &lt;a href=&quot;https://github.com/RamblingCookieMonster/SecretServer&quot;&gt;Secret Server&lt;/a&gt; and &lt;a href=&quot;https://github.com/devblackops/PasswordState&quot;&gt;PasswordState&lt;/a&gt; already have community PowerShell modules wrapping their APIs.  Sort of important for this last bullet:&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;How fast can you change your passwords?&lt;/strong&gt;  John left.  He’s angry and has always been a bit rash.  He has all the passwords, because you used KeePass, and you don’t know which passwords he’s accessed before he left.  Or, maybe Jane hacked through your defenses and made off with your passwords.  Whatever the case:  Can you cycle all your passwords?  How quickly?  Will you cause an outage?  A real password management solution can help with this, by providing a robust API, perhaps natively hooking into certain technologies for automated credential changes, providing audit logs that tell you which passwords John accessed to limit the scope of these changes, etc.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Anyhow!  That was completely tangential, but I miss having a real password management solution, and the automation this enables.&lt;/p&gt;

&lt;p&gt;That brings us to todays topic:  &lt;em&gt;Shoot.  What’s that account’s password again?  It’s running a scheduled task, surely we can find it!&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;scheduled-task-credentials&quot;&gt;Scheduled Task Credentials&lt;/h2&gt;

&lt;p&gt;Sometimes it can come in handy to extract a password from a scheduled task.  Perhaps you forgot to record or update it, because you’re not automating these things yet.  Perhaps someone changed it manually and your monitoring hasn’t picked up the mismatch between your password solution’s idea of the password, and the actual password.&lt;/p&gt;

&lt;p&gt;I ran into one of these cases, and needed to find an account’s password, without changing it.  I’m not going to go into the technical details on how or why this works, I’ll leave that to you.  Here’s a quick walk through on extracting a password from a scheduled task:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Have privileges on the system&lt;/li&gt;
  &lt;li&gt;Download &lt;a href=&quot;https://technet.microsoft.com/en-us/sysinternals/bb897553.aspx&quot;&gt;psexec&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Download &lt;a href=&quot;http://www.nirsoft.net/utils/network_password_recovery.html&quot;&gt;nirsoft’s netpass&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Use psexec to launch netpass in SYSTEM’s context&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# psexec is somewhere in my $ENV:Path&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# netpass is in the root of C:\&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# adjust as needed&lt;/span&gt;
psexec -i -s -d C:\netpass.exe
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href=&quot;/images/quick/password.png&quot;&gt;&lt;img src=&quot;/images/quick/password.png&quot; alt=&quot;Passwords&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Voila!  I have the password for this fake scheduled task.&lt;/p&gt;

&lt;h3 id=&quot;wrapping-up&quot;&gt;Wrapping up&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/gentilkiwi/mimikatz&quot;&gt;Tools&lt;/a&gt; and &lt;a href=&quot;https://www.securusglobal.com/community/2013/12/20/dumping-windows-credentials/&quot;&gt;references&lt;/a&gt; &lt;a href=&quot;https://adsecurity.org/?p=556&quot;&gt;abound&lt;/a&gt; - Do consider consulting your $Boss before taking this route.  Just in case.&lt;/p&gt;

&lt;p&gt;Cheers!&lt;/p&gt;

    &lt;p&gt;&lt;a href=&quot;http://ramblingcookiemonster.github.io/Quick-Hits-and-Some-Mischief/&quot;&gt;Quick Hit&amp;#58; Extract Scheduled Task Credentials&lt;/a&gt; was originally published by Warren F at &lt;a href=&quot;http://ramblingcookiemonster.github.io&quot;&gt;Rambling Cookie Monster&lt;/a&gt; on September 09, 2016.&lt;/p&gt;
  </content>
</entry>


<entry>
  <title type="html"><![CDATA[Invoke PowerShell on Azure VMs]]></title>
  <link rel="alternate" type="text/html" href="http://ramblingcookiemonster.github.io/Invoke-AzureRmVmScript/" />
  <id>http://ramblingcookiemonster.github.io/Invoke-AzureRmVmScript</id>
  <updated>2016-07-21T07:00:00+00:00</updated>
  <published>2016-07-21T07:00:00+00:00</published>
  
  <author>
    <name>Warren F</name>
    <uri>http://ramblingcookiemonster.github.io</uri>
    
  </author>
  <content type="html">
    &lt;section id=&quot;table-of-contents&quot; class=&quot;toc&quot;&gt;
  &lt;header&gt;
    &lt;h3&gt;&lt;i class=&quot;fa fa-book&quot;&gt;&lt;/i&gt; Overview&lt;/h3&gt;
  &lt;/header&gt;
&lt;div id=&quot;drawer&quot;&gt;
&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#rambling&quot; id=&quot;markdown-toc-rambling&quot;&gt;Rambling&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#clones&quot; id=&quot;markdown-toc-clones&quot;&gt;Clones&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#public-ips&quot; id=&quot;markdown-toc-public-ips&quot;&gt;Public IPs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#catastropheish&quot; id=&quot;markdown-toc-catastropheish&quot;&gt;Catastrophe(ish)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#invoke-a-script&quot; id=&quot;markdown-toc-invoke-a-script&quot;&gt;Invoke a Script&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#invoke-azurermvmscript&quot; id=&quot;markdown-toc-invoke-azurermvmscript&quot;&gt;Invoke-AzureRmVmScript&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#investigating-output&quot; id=&quot;markdown-toc-investigating-output&quot;&gt;Investigating Output&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#parallelize&quot; id=&quot;markdown-toc-parallelize&quot;&gt;Parallelize!&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

  &lt;/div&gt;
&lt;/section&gt;
&lt;!-- /#table-of-contents --&gt;

&lt;h2 id=&quot;rambling&quot;&gt;Rambling&lt;/h2&gt;

&lt;p&gt;I cut my teeth in a conservative enterprise environment.  Few, if any services were hosted.  I’m now in an organization that’s very open to hosted services where they make sense.&lt;/p&gt;

&lt;p&gt;Need to spin up a bunch of temporary compute?  Use something like Azure or AWS, rather than going through the time and effort to provision physical resources that won’t necessarily have a purpose in a few weeks, assuming our processes for physical equipment are even efficient enough to get these up and running in time for the customer’s deadline.&lt;/p&gt;

&lt;p&gt;So!  I had my first go at this the other week.  There’s a deadline coming up, and a team is a bit behind on generating some data.  We have plenty of *nix compute, but they have this wonderful single threaded, Windows-specific workload to run.&lt;/p&gt;

&lt;p&gt;If only we had a service where we could spin up a bunch of VMs.  Oh!  We have an Azure account.  If only we had the budget.  Oh!  We might have credits, or maybe this actually made the budget.&lt;/p&gt;

&lt;p&gt;If only we had experience spinning up legacy Windows systems in the cloud, with no connectivity to our configuration management platform.  Time to improvise and make a few mistakes!&lt;/p&gt;

&lt;p&gt;This is a quick hit on my experience with a similar scenario, that will likely expose my inexperience with Azure, and hosted services in general.&lt;/p&gt;

&lt;h2 id=&quot;clones&quot;&gt;Clones&lt;/h2&gt;

&lt;p&gt;First things first!  We spin up a single Azure VM using the legacy Server 2008 R2 OS.  Requirements dictate this, sadly.  Testing goes well.  &lt;em&gt;We’ll take 50 of them!&lt;/em&gt; our customer enthusiastically exclaims.  After validating that yes, they actually do need 50, selecting an optimal VM size, and configuring the image, we’re off to the races.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/Azure/azure-quickstart-templates&quot;&gt;Azure quickstart templates&lt;/a&gt; look fantastic, and I’ve heard great things, but time is tight, so I borrow some code from Stephane Lapointe to &lt;a href=&quot;http://www.codeisahighway.com/how-to-capture-your-own-custom-virtual-machine-image-under-azure-resource-manager-api/&quot;&gt;capture a template&lt;/a&gt; and &lt;a href=&quot;http://www.codeisahighway.com/how-to-create-a-virtual-machine-from-a-custom-image-using-arm-and-azure-powershell-v1-0-x/&quot;&gt;create VMs&lt;/a&gt; (similar content from Microsoft: &lt;a href=&quot;https://azure.microsoft.com/en-us/documentation/articles/virtual-machines-windows-classic-capture-image/&quot;&gt;capture&lt;/a&gt;, &lt;a href=&quot;https://azure.microsoft.com/en-us/documentation/articles/virtual-machines-windows-classic-createportal/&quot;&gt;create&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;There are a few tweaks to make: we use &lt;a href=&quot;https://azure.microsoft.com/en-us/documentation/articles/virtual-networks-nsg/&quot;&gt;network security groups&lt;/a&gt;, and we already have a virtual network to use, but by and large our code is similar, outside of &lt;a href=&quot;http://ramblingcookiemonster.github.io/Trust-but-Verify/&quot;&gt;some sanitation and error handling&lt;/a&gt; to avoid a mess.  We spin up the first clone.&lt;/p&gt;

&lt;p&gt;Oh.  That VM took over five minutes to create.  I’m new to this, so I want to watch things progress to see if any issues come up.  I’m not waiting 50 * 5 minutes.  Runspaces are a thing.  Usually I would go with Boe Prox’s fantastic &lt;a href=&quot;https://github.com/proxb/PoshRSJob&quot;&gt;PoshRsJob&lt;/a&gt;, but this is a quick hit, so I go with &lt;a href=&quot;https://github.com/RamblingCookieMonster/Invoke-Parallel&quot;&gt;Invoke-Parallel&lt;/a&gt;.  Someone mentioned that they had seen it &lt;a href=&quot;https://blog.kloud.com.au/2016/02/10/synchronously-startstop-all-azure-resource-manager-virtual-machines-in-a-resource-group/&quot;&gt;used with Azure&lt;/a&gt;, so I wrapped my code in Invoke-Parallel, and kicked things off.&lt;/p&gt;

&lt;p&gt;Nice!  Fifty VMs up and running.  But… how do I get to them?&lt;/p&gt;

&lt;h2 id=&quot;public-ips&quot;&gt;Public IPs&lt;/h2&gt;

&lt;p&gt;This has to be simple.  There are so many AzureRm Cmdlets, We must be able to pull details on VMs along with their public IPs.&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Get-AzureRmVm @params | Get-AzureRmPublicIP @otherparams
&lt;span class=&quot;c1&quot;&gt;# Nope&lt;/span&gt;

Get-AzureRmVm @params | Get-AzureRmNetworkInterface @otherparams
&lt;span class=&quot;c1&quot;&gt;# Nope&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Huh.  There’s no PowerShell-y way to do this that I can find.  That’s sad.  Guess we’ll write a function!  &lt;a href=&quot;https://github.com/RamblingCookieMonster/PowerShell/blob/master/Get-AzureRmVmPublicIP.ps1&quot;&gt;Get-AzureRmVmPublicIp&lt;/a&gt;&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Get-AzureRmVmPublicIP -ResourceGroupName 'my-resource-group'

# VMName  NICName    PublicIP
# ------  -------    --------
# VM-2    VM-2-NIC   23.96.1.2
# VM-3    VM-3-NIC   23.96.1.3
# VM-4    VM-4-NIC   168.61.2.1
# VM-16   VM-16-NIC  168.61.10.27
# VM-17   VM-17-NIC  23.96.17.56
# VM-18   VM-18-NIC  23.96.19.71
# VM-1    VM-1-NIC   Not Assigned
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Perfect!  We can now get a list of VMs and public IPs.  Our customer doesn’t have access to Azure, that would likely be a better solution here, but what can you do.&lt;/p&gt;

&lt;h2 id=&quot;catastropheish&quot;&gt;Catastrophe(ish)&lt;/h2&gt;

&lt;p&gt;Our customer is quite thankful.  Their work is chugging along on day one.  We continue our various other projects and tasks.  Ideally we might have something like &lt;a href=&quot;https://azure.microsoft.com/en-us/documentation/articles/operations-management-suite-overview/&quot;&gt;OMS&lt;/a&gt; up and running to watch these VMs.  Thankfully, one of our astute customers notices when one of these VMs restart overnight.&lt;/p&gt;

&lt;p&gt;We should probably mention here that when you run a workload in the cloud, that workload should be designed appropriately.  If you have a system sensitive to restarts running on Azure, AWS, or any other hosted service, you’re going to have a bad time.  Alas, we’re talking code written by and for a unique audience; it can’t handle multiple cores, let alone the many scenarios that come up in a hosted environment.&lt;/p&gt;

&lt;p&gt;What happened?  The sysprep process re-enabled automatic updates.  Lesson: If you don’t use configuration management, you need to think carefully about all of the things you previously configured and take for granted.&lt;/p&gt;

&lt;p&gt;No problem!  We’ll just use PowerShell remoting to fix the config, and to find systems that restarted to give the customer a nice report on systems they’ll need to visit.  Oh.  Server 2008 R2.  Remoting isn’t enabled out of the box.  And our group policy isn’t applied.&lt;/p&gt;

&lt;p&gt;So!  A few lessons.  Planning is important.  Had we done this before, and not been under a deadline of a day or so, hopefully we would have addressed these.  Being able to manage your systems is somewhat important.&lt;/p&gt;

&lt;p&gt;That’s fine, Microsoft learned from VMware’s Invoke-VMScript (presumably) and recently gave us &lt;a href=&quot;https://msdn.microsoft.com/en-us/virtualization/hyperv_on_windows/user_guide/vmsession?f=255&amp;amp;MSPPError=-2147217396&quot;&gt;PowerShell Direct&lt;/a&gt;.  Surely this, or something similar is available in Azure.&lt;/p&gt;

&lt;p&gt;Nope nope nope.&lt;/p&gt;

&lt;h2 id=&quot;invoke-a-script&quot;&gt;Invoke a Script&lt;/h2&gt;

&lt;p&gt;That’s all I want.  To invoke a script on a VM.  I look around.  I read about &lt;a href=&quot;https://azure.microsoft.com/en-us/documentation/articles/virtual-machines-windows-classic-extensions-customscript/&quot;&gt;custom script extensions&lt;/a&gt;.  Interesting: upload a file, add the extension to a VM, not terribly complicated.  Oh.  You can only have one per VM.  Any other options?&lt;/p&gt;

&lt;p&gt;I ask around.  I join an &lt;a href=&quot;azured.io&quot;&gt;Azure Slack team&lt;/a&gt;.  Crickets.  Someone finally responds:  Azure Runbooks or Hybrid Runbook Workers might work.  I could be wrong, but I couldn’t find anything that would let me run a PowerShell script on an Azure VM, without configuring remoting or registering a hybrid runbook worker on each VM.&lt;/p&gt;

&lt;p&gt;Back to custom script extensions.  We can deal with the limitations.  A few minutes later, updates are disabled.  The customer already ran through and checked for restarted systems, so they’re good to go, no list needed.&lt;/p&gt;

&lt;p&gt;I have this nagging feeling.  I need to be able to run a command and get the output back.  We’re not about to RDP into 50 systems manually if something else comes up.  We already have the building blocks from executing the CustomScriptExtension to configure updates, all we need to do is abstract out each step of the process.&lt;/p&gt;

&lt;p&gt;I check the Azure Slack team one more time.  I’m a fan of using existing libraries, and contributing to them if they need a bit more functionality.  No luck.&lt;/p&gt;

&lt;p&gt;I spend a few minutes and put together &lt;a href=&quot;https://github.com/RamblingCookieMonster/PowerShell/blob/master/Invoke-AzureRmVmScript.ps1&quot;&gt;Invoke-AzureRmVmScript&lt;/a&gt;.  I test it on one VM.  It works.  I test it with Invoke-Parallel.  It works.  Yay!&lt;/p&gt;

&lt;h3 id=&quot;invoke-azurermvmscript&quot;&gt;Invoke-AzureRmVmScript&lt;/h3&gt;

&lt;p&gt;We boil everything down into a few high level steps:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/RamblingCookieMonster/PowerShell/blob/aecea7781ee82141a6990e39594f43dfb46edb67/Invoke-AzureRmVmScript.ps1#L168&quot;&gt;Check the VM for existing CustomScriptExtensions&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/RamblingCookieMonster/PowerShell/blob/aecea7781ee82141a6990e39594f43dfb46edb67/Invoke-AzureRmVmScript.ps1#L197&quot;&gt;Upload our script to an Azure storage account&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/RamblingCookieMonster/PowerShell/blob/aecea7781ee82141a6990e39594f43dfb46edb67/Invoke-AzureRmVmScript.ps1#L267&quot;&gt;Set the CustomScriptExtension on the VM&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/RamblingCookieMonster/PowerShell/blob/aecea7781ee82141a6990e39594f43dfb46edb67/Invoke-AzureRmVmScript.ps1#L294&quot;&gt;Read the output from the script&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is a quick-publish, so more work to do, but if you’re looking for a simple way to invoke PowerShell on an Azure VM, without using PowerShell remoting or installing a worker on the VM, &lt;a href=&quot;https://github.com/RamblingCookieMonster/PowerShell/blob/master/Invoke-AzureRmVmScript.ps1&quot;&gt;Invoke-AzureRmVmScript&lt;/a&gt; might do the trick.&lt;/p&gt;

&lt;p&gt;Let’s look at a few examples!&lt;/p&gt;

&lt;h3 id=&quot;investigating-output&quot;&gt;Investigating Output&lt;/h3&gt;

&lt;p&gt;PowerShell lets us send output to &lt;a href=&quot;https://blogs.technet.microsoft.com/heyscriptingguy/2014/03/30/understanding-streams-redirection-and-write-host-in-powershell/&quot;&gt;various streams&lt;/a&gt;.  I wonder which we can get back from Azure?&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &lt;span class=&quot;nv&quot;&gt;$params&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; @&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        ResourceGroupName &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'My-Resource-Group'&lt;/span&gt;
        VMName &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'VM-22'&lt;/span&gt;
        StorageAccountName &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'storageaccountname'&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    Invoke-AzureRmVmScript @params -ScriptBlock &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&quot;Hello world! Running on &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;$(&lt;/span&gt;hostname&lt;span class=&quot;k&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;Write-Error&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;This is an error&quot;&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;Write-Warning&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;This is a warning&quot;&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;Write-Verbose&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;This is verbose!&quot;&lt;/span&gt; -Verbose
        &lt;span class=&quot;nb&quot;&gt;Write-Host&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;This is killing a kitten&quot;&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I wait patiently (this is not fast, Set-AzureRmVmCustomScriptExtension takes some time):&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ResourceGroupName : My-Resource-Group
VMName            : VM-22
Substatuses       : {Microsoft.Azure.Management.Compute.Models.InstanceViewStatus, Microsoft.Azure.Management.Compute.Models.InstanceViewStatus}
StdOut_succeeded  : Hello world! Running on HF-22\nWARNING: This is a warning\nVERBOSE: This is verbose!\nThis is killing a kitten
StdErr_succeeded  : C:\Packages\Plugins\Microsoft.Compute.CustomScriptExtension\1.8\Downloads\0\017\n21f1b_7a06_4a45_8d12_3974b59deaf5.ps1 : This is an error\n    + CategoryInfo 
                             : NotSpecified: (:) [Write-Error], WriteErrorExcep \n   tion\n    + FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorExceptio \n   
                    n,01721f1b_7a06_4a45_8d12_3974b59deaf5.ps1\n 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Yuck!  Unfortunately, we’re getting plain text back here, including special characters like \n which would typically be represented by an actual new line.  We might write something to serialize output to JSON if we were so inclined, and were stuck on PowerShell 2 without &lt;a href=&quot;https://technet.microsoft.com/en-us/library/hh849922.aspx&quot;&gt;ConvertTo-Json&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;What did we get?  Pretty much everything went to StdOut - Output, Warning, Verbose, and Information streams were all captured.  As expected, the error stream went to StdErr.&lt;/p&gt;

&lt;p&gt;So! That’s one VM, and it took a bit over a minute to run.  That’s painful.&lt;/p&gt;

&lt;h3 id=&quot;parallelize&quot;&gt;Parallelize!&lt;/h3&gt;

&lt;p&gt;I’m impatient when it comes to running code.  Waiting for AppVeyor to &lt;a href=&quot;ramblingcookiemonster.github.io/PSDeploy-Inception/&quot;&gt;queue, test, and deploy a module&lt;/a&gt; occasionally induces twitching.&lt;/p&gt;

&lt;p&gt;In this case, there’s a simple solution: runspaces!  Not writing our own, that can be painful, but borrowing something like &lt;a href=&quot;https://github.com/proxb/PoshRSJob&quot;&gt;PoshRsJob&lt;/a&gt;.  We’ll use &lt;a href=&quot;https://github.com/RamblingCookieMonster/Invoke-Parallel&quot;&gt;Invoke-Parallel&lt;/a&gt; for this simple example:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Get all the VMs in our resource group where the agent status is ready&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$ResourceGroupName&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'My-Resource-Group'&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$StorageAccountName&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'mystorageaccount'&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$StorageAccountKey&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'my storage account key in plain text'&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# you can omit this and we'll pull it for you...&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$VMs&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; Get-AzureRmVM -ResourceGroupName &lt;span class=&quot;nv&quot;&gt;$ResourceGroupName&lt;/span&gt; |
    &lt;span class=&quot;k&quot;&gt;Foreach&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        Get-AzureRmVm -ResourceGroupName &lt;span class=&quot;nv&quot;&gt;$ResourceGroupName&lt;/span&gt; -Name &lt;span class=&quot;nv&quot;&gt;$_&lt;/span&gt;.Name -Status
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; |
    &lt;span class=&quot;nb&quot;&gt;Where&lt;/span&gt;-Object &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$_&lt;/span&gt;.VMAgent.Statuses[0].DisplayStatus -like &lt;span class=&quot;s1&quot;&gt;'Ready'&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; |
    &lt;span class=&quot;nb&quot;&gt;Select&lt;/span&gt; -ExpandProperty Name

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

&lt;p&gt;Basically, I want all VMs in my resource group that have a VMAgent that is ready.  I have my VMs, time to play!&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Kick off script on VMs in parallel&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# 50 at a time, timeout at 5 minutes, pull in variables&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;$InvokeParallelParams&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; @&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    RunspaceTimeout &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;60&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;5&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
    Throttle &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; 50
    InputObject &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$VMs&lt;/span&gt;
    ImportVariables &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$true&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;$Output&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; Invoke-Parallel @InvokeParallelParams -ScriptBlock &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# Load Invoke-AzureRmVmScript. Alternatively you could hard code it here, or we could fix Invoke-Parallel to pull in functions&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'C:\Invoke-AzureRmVmScript.ps1'&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# Parameters to splat&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$params&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; @&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        ResourceGroupName &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$ResourceGroupName&lt;/span&gt;
        VMName &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$_&lt;/span&gt;
        StorageAccountName &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$StorageAccountName&lt;/span&gt;
        StorageAccountKey &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$StorageAccountKey&lt;/span&gt;
        Force &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$True&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    Invoke-AzureRmVmScript @params -ScriptBlock &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;$Processes&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Get-Process&lt;/span&gt; -Name ExampleProcess
        &lt;span class=&quot;s2&quot;&gt;&quot;'&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$Processes&lt;/span&gt;.count&lt;span class=&quot;k&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;' ExampleProcess processes on &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;$(&lt;/span&gt;hostname&lt;span class=&quot;k&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It will still take some time, but this beats running things serially!  It looks like at least one VM is ready to tear down:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$Output

VMName StdOut_succeeded
------ ----------------
VM-12  '' ExampleProcess processes on VM-12
VM-21  '4' ExampleProcess processes on VM-21
VM-18  '5' ExampleProcess processes on VM-18
VM-40  '5' ExampleProcess processes on VM-40
VM-27  '6' ExampleProcess processes on VM-27
VM-3   '6' ExampleProcess processes on VM-3
VM-51  '6' ExampleProcess processes on VM-51
VM-52  '6' ExampleProcess processes on VM-52
VM-6   '6' ExampleProcess processes on VM-6
...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That’s about it!&lt;/p&gt;

&lt;p&gt;Going forward, we’ll have more time to plan these out, and connectivity should be available for important things like configuration management and authentication, but if we ever need it, we’ll have a quick tool to hit poorly-setup-systems.  Perhaps we could bootstrap remoting or other connectivity with this function, but these VMs are already reaching their end of life.&lt;/p&gt;

&lt;p&gt;And thus ends my first experience working with resources in the cloud.  It’s been fun, looking forward to more!&lt;/p&gt;

&lt;p&gt;Side note: the brown cloud illustrates my ability to work with the cloud, and is not a reflection on Azure. Okay, maybe it’s a hyperbolic reflection on the documentation, a few bugs I ran into, and some not-as-PowerShell-y-as-expected behavior.&lt;/p&gt;

    &lt;p&gt;&lt;a href=&quot;http://ramblingcookiemonster.github.io/Invoke-AzureRmVmScript/&quot;&gt;Invoke PowerShell on Azure VMs&lt;/a&gt; was originally published by Warren F at &lt;a href=&quot;http://ramblingcookiemonster.github.io&quot;&gt;Rambling Cookie Monster&lt;/a&gt; on July 21, 2016.&lt;/p&gt;
  </content>
</entry>


<entry>
  <title type="html"><![CDATA[A PowerShell Module Release Pipeline]]></title>
  <link rel="alternate" type="text/html" href="http://ramblingcookiemonster.github.io/PSDeploy-Inception/" />
  <id>http://ramblingcookiemonster.github.io/PSDeploy-Inception</id>
  <updated>2016-07-14T12:00:00+00:00</updated>
  <published>2016-07-14T12:00:00+00:00</published>
  
  <author>
    <name>Warren F</name>
    <uri>http://ramblingcookiemonster.github.io</uri>
    
  </author>
  <content type="html">
    &lt;section id=&quot;table-of-contents&quot; class=&quot;toc&quot;&gt;
  &lt;header&gt;
    &lt;h3&gt;&lt;i class=&quot;fa fa-book&quot;&gt;&lt;/i&gt; Overview&lt;/h3&gt;
  &lt;/header&gt;
&lt;div id=&quot;drawer&quot;&gt;
&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#rambling&quot; id=&quot;markdown-toc-rambling&quot;&gt;Rambling&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#why-continuous-deployment-for-modules&quot; id=&quot;markdown-toc-why-continuous-deployment-for-modules&quot;&gt;Why Continuous Deployment for Modules?&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#the-ingredients&quot; id=&quot;markdown-toc-the-ingredients&quot;&gt;The Ingredients&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#the-recipe&quot; id=&quot;markdown-toc-the-recipe&quot;&gt;The Recipe&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#following-the-recipe&quot; id=&quot;markdown-toc-following-the-recipe&quot;&gt;Following the Recipe&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#appveyor-and-powershell-gallery&quot; id=&quot;markdown-toc-appveyor-and-powershell-gallery&quot;&gt;AppVeyor and PowerShell Gallery&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#release-pipeline-scaffolding&quot; id=&quot;markdown-toc-release-pipeline-scaffolding&quot;&gt;Release Pipeline Scaffolding&lt;/a&gt;        &lt;ul&gt;
          &lt;li&gt;&lt;a href=&quot;#appveyoryml&quot; id=&quot;markdown-toc-appveyoryml&quot;&gt;AppVeyor.yml&lt;/a&gt;&lt;/li&gt;
          &lt;li&gt;&lt;a href=&quot;#buildps1&quot; id=&quot;markdown-toc-buildps1&quot;&gt;Build.ps1&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#psakeps1&quot; id=&quot;markdown-toc-psakeps1&quot;&gt;Psake.ps1&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#deploypsdeployps1&quot; id=&quot;markdown-toc-deploypsdeployps1&quot;&gt;Deploy.PSDeploy.ps1&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#what-happens-now&quot; id=&quot;markdown-toc-what-happens-now&quot;&gt;What Happens Now?&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#rambling-outro-the-gallery&quot; id=&quot;markdown-toc-rambling-outro-the-gallery&quot;&gt;Rambling Outro: The Gallery&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

  &lt;/div&gt;
&lt;/section&gt;
&lt;!-- /#table-of-contents --&gt;

&lt;h2 id=&quot;rambling&quot;&gt;Rambling&lt;/h2&gt;

&lt;p&gt;I like this new Microsoft.  Not only are they open sourcing more components, taking pull requests, and contributing upstream, they’re doing a great job reaching out and interacting with the community.&lt;/p&gt;

&lt;p&gt;In the past year or so, I went from occasionally throwing code on GitHub, to collaborating on open source projects, sharing what I learned, and somehow having a fun project of mine appear in a few sessions on &lt;a href=&quot;http://aka.ms/thereleasepipelinemodel&quot;&gt;release pipelines&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://gist.github.com/RamblingCookieMonster/89fb436e27dd715a2d0e2c5380f2008f&quot;&gt;&lt;img src=&quot;/images/deploy/community.pipeline.projects.jpg&quot; alt=&quot;Release Pipeline Projects&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I owe much of this to &lt;a href=&quot;https://twitter.com/xvorsx&quot;&gt;Sergei V.&lt;/a&gt; and &lt;a href=&quot;https://twitter.com/migreene&quot;&gt;Michael G.&lt;/a&gt;, two Microsoftees who helped motivate and prod me (and others) along the way.&lt;/p&gt;

&lt;p&gt;This post is a follow up to &lt;a href=&quot;http://ramblingcookiemonster.github.io/GitHub-For-PowerShell-Projects/#continuous-integration&quot;&gt;the series on GitHub, Pester, and AppVeyor&lt;/a&gt;, and the post on &lt;a href=&quot;http://ramblingcookiemonster.github.io/Building-A-PowerShell-Module/&quot;&gt;building PowerShell modules&lt;/a&gt;.  We’re going to wrap up the module writing process and demonstrate &lt;em&gt;how to automatically deploy your modules to the PowerShell Gallery&lt;/em&gt;.&lt;/p&gt;

&lt;h2 id=&quot;why-continuous-deployment-for-modules&quot;&gt;Why Continuous Deployment for Modules?&lt;/h2&gt;

&lt;p&gt;The importance of writing modules &lt;a href=&quot;http://ramblingcookiemonster.github.io/Building-A-PowerShell-Module/#why-modules&quot;&gt;is covered here&lt;/a&gt;.  This misses a few pieces of the puzzle though:  now that we have a module up on GitHub and published to the gallery, we’re not quite done.  If we want to reap some of the &lt;a href=&quot;http://www.themacro.com/articles/2016/05/why-the-best-give-away/&quot;&gt;benefits of open source&lt;/a&gt;, we need to make it easy to pull in suggestions from helpful folks on the Internet.&lt;/p&gt;

&lt;p&gt;For those of you writing modules, how often have you taken a pull request or committed a change, thinking &lt;em&gt;I’ll get around to publishing this on the PowerShell gallery later&lt;/em&gt;?  Publishing to the gallery is fairly painless, but it’s another thing to do and to remember.  We’re all human, so we occasionally forget, or get lazy and skip that step.&lt;/p&gt;

&lt;p&gt;You might even add those extra steps to your calculation of how long it will take to integrate someone’s suggestions.  &lt;em&gt;I’ll have to merge that in, and do a bunch of things afterwards to publish it, I’ll just tackle it all some other time&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Having a pipeline to automatically test and deploy our changes can remove the barriers that might dissuade us from these changes.  It’s also a great learning opportunity for CI/CD ideas that you could apply to your infrastructure.&lt;/p&gt;

&lt;p&gt;So!  Where do we start?&lt;/p&gt;

&lt;h2 id=&quot;the-ingredients&quot;&gt;The Ingredients&lt;/h2&gt;

&lt;p&gt;Let’s look at the ingredients for a recipe that gives us continuous deployment for a PowerShell module.  We’ll use content from PSDeploy itself as an example:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;A module built roughly following &lt;a href=&quot;http://ramblingcookiemonster.github.io/Building-A-PowerShell-Module/&quot;&gt;this guide&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;A GitHub account&lt;/li&gt;
  &lt;li&gt;An AppVeyor account (sign in with GitHub)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.powershellgallery.com/&quot;&gt;A PowerShell gallery account&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Your PowerShell gallery key, encrypted by AppVeyor&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/RamblingCookieMonster/PSDeploy/blob/f813a8ba39702cf446fe0b23994e18936412ea9c/appveyor.yml&quot;&gt;appveyor.yml&lt;/a&gt;.  This tells AppVeyor what to run when you make a change to your module&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/RamblingCookieMonster/PSDeploy/blob/f813a8ba39702cf446fe0b23994e18936412ea9c/build.ps1&quot;&gt;Build.ps1&lt;/a&gt;.  This is a simple script to pull in dependencies and kick off psake, which does the real work&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/RamblingCookieMonster/PSDeploy/blob/ab04c9c4122d520b475711f254fb65e520e212e8/psake.ps1&quot;&gt;psake.ps1&lt;/a&gt;.  This organizes your build into “tasks”.  It runs your tests and deployments&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/RamblingCookieMonster/PSDeploy/blob/9b14a0807e1aa52b4df8e41ac5b66de8f63a3f2e/deploy.psdeploy.ps1&quot;&gt;deploy.psdeploy.ps1&lt;/a&gt;.  This tells PSDeploy how to deploy your project - in this case, publishing a module&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s put these together!&lt;/p&gt;

&lt;h2 id=&quot;the-recipe&quot;&gt;The Recipe&lt;/h2&gt;

&lt;p&gt;Let’s boil this down into a few steps:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://ramblingcookiemonster.github.io/Building-A-PowerShell-Module/&quot;&gt;Create your module&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Sign up for AppVeyor and PowerShellGallery.com&lt;/li&gt;
  &lt;li&gt;Add scaffolding for a release pipeline&lt;/li&gt;
  &lt;li&gt;Profit&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s it! Once you get your module set up from the previous article, you can just layer a little scaffolding on top to automatically publish to the PowerShell gallery.&lt;/p&gt;

&lt;h2 id=&quot;following-the-recipe&quot;&gt;Following the Recipe&lt;/h2&gt;

&lt;h3 id=&quot;appveyor-and-powershell-gallery&quot;&gt;AppVeyor and PowerShell Gallery&lt;/h3&gt;

&lt;p&gt;So!  You should already have a GitHub account.  We need two more accounts:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Register at &lt;a href=&quot;https://www.powershellgallery.com/&quot;&gt;PowerShellGallery.com&lt;/a&gt;.  Sign in&lt;/li&gt;
  &lt;li&gt;Copy your PowerShell Gallery API key from &lt;a href=&quot;https://www.powershellgallery.com/account&quot;&gt;your account page&lt;/a&gt;.  Keep this key a secret&lt;/li&gt;
  &lt;li&gt;Sign in to &lt;a href=&quot;http://www.appveyor.com/&quot;&gt;AppVeyor&lt;/a&gt; with your GitHub account&lt;/li&gt;
  &lt;li&gt;Create a &lt;a href=&quot;https://www.appveyor.com/docs/build-configuration#secure-variables&quot;&gt;secure variable&lt;/a&gt;: Click your AppVeyor account drop down, Encrypt data.  Paste in your API key and Encrypt!  Copy out the resulting encrypted value&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Note that &lt;a href=&quot;http://docs.gitlab.com/ee/ci/variables/README.html&quot;&gt;GitLab CI&lt;/a&gt; and other build systems often have ways to inject secure data like this.&lt;/p&gt;

&lt;p&gt;That’s it for the GUI stuff, it’s time to dive into some PowerShell!&lt;/p&gt;

&lt;h3 id=&quot;release-pipeline-scaffolding&quot;&gt;Release Pipeline Scaffolding&lt;/h3&gt;

&lt;p&gt;We’re going to use the following components for our release pipeline:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;AppVeyor.yml.  Instructions for AppVeyor.  We’ll still use this, but we’ll try to move as much of the build as possible into PowerShell tooling that will work in other build systems.&lt;/li&gt;
  &lt;li&gt;Build.ps1.  A build script that sets up our dependencies and kicks off psake.  Portable across build systems.  We install and use a few dependencies:
    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;https://github.com/RamblingCookieMonster/BuildHelpers&quot;&gt;BuildHelpers&lt;/a&gt;.  A module to help with portability and some common build needs&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;https://github.com/psake/psake&quot;&gt;Psake&lt;/a&gt;.  A build automation tool.  Lets us define a series of tasks for our build&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;https://github.com/pester/Pester&quot;&gt;Pester&lt;/a&gt;.  A testing framework for PowerShell&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;https://github.com/RamblingCookieMonster/PSDeploy&quot;&gt;PSDeploy&lt;/a&gt;.  A module to simplify PowerShell based deployments - Modules, in this case&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Psake.ps1.  Tasks to run - testing, build (e.g. bump version number), and deployment to the PowerShell gallery&lt;/li&gt;
  &lt;li&gt;deploy.psdeploy.ps1.  Instructions that tell PSDeploy how to deploy our module&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This combination meets two goals:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;It’s generalized and can be dropped into a new project&lt;/li&gt;
  &lt;li&gt;Outside of adding a build-system-config that kicks off build.ps1, it can be used on build systems like Jenkins, GitLab CI, or even your own PC&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This might seem complex, but it’s really just a few steps - let’s walk through the code:&lt;/p&gt;

&lt;h4 id=&quot;appveyoryml&quot;&gt;AppVeyor.yml&lt;/h4&gt;

&lt;p&gt;There are two key bits in &lt;a href=&quot;https://github.com/RamblingCookieMonster/PSDeploy/blob/f813a8ba39702cf446fe0b23994e18936412ea9c/appveyor.yml&quot;&gt;the yaml&lt;/a&gt;:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;environment&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;NuGetApiKey&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;secure&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;oqMFzG8F65K5l572V7VzlZIWU7xnSYDLtSXECJAAURrXe8M2+BAp9vHLT+1h1lR0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This decrypts our API key and creates an environmental value that we can call in PowerShell as &lt;code class=&quot;highlighter-rouge&quot;&gt;$ENV:NuGetApiKey&lt;/code&gt;.  Be careful not to display this in any output.&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;test_script&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ps&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;. .\build.ps1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This runs our build.ps1 file.  AppVeyor runs from the project root, so we know the relative path to this script.&lt;/p&gt;

&lt;h4 id=&quot;buildps1&quot;&gt;Build.ps1&lt;/h4&gt;

&lt;p&gt;In build.ps1, we use Resolve-Module from Brandon Padgett to pull in dependencies: BuildHelpers, psake, Pester, and PSDeploy:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Grab nuget bits, install modules, set build variables, start build.&lt;/span&gt;
Get-PackageProvider -Name NuGet -ForceBootstrap | Out-Null

Resolve-Module Psake, PSDeploy, Pester, BuildHelpers
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Once these are in place, we use &lt;code class=&quot;highlighter-rouge&quot;&gt;Set-BuildEnvironment&lt;/code&gt; to create some environment variables for our project.  Here’s some example output:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Name                 Value
----                 -----
BHProjectName        psdeploy
BHProjectPath        C:\projects\psdeploy
BHPSModuleManifest   C:\projects\psdeploy\psdeploy\psdeploy.psd1
BHPSModulePath       C:\projects\psdeploy\psdeploy
BHCommitMessage      !Deploy Brandon's changes
BHBuildSystem        AppVeyor
BHBranchName         master
BHBuildNumber        132
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You can use these details to simplify the rest of your build code, and to normalize variables across build systems.  At the moment BuildHelpers supports AppVeyor, Jenkins, GitLab CI, and VSTS (Thanks Stijn!); pull requests would be welcome.&lt;/p&gt;

&lt;p&gt;Lastly, we kick of psake, and if it fails (e.g. a failed test), we exit with a non-zero code to tell our build system that we didn’t succeed.&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Invoke-psake .\psake.ps1
&lt;span class=&quot;k&quot;&gt;exit&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;](&lt;/span&gt; -not &lt;span class=&quot;nv&quot;&gt;$psake&lt;/span&gt;.build_success &lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;psakeps1&quot;&gt;Psake.ps1&lt;/h3&gt;

&lt;p&gt;This is where the real work starts to happen.&lt;/p&gt;

&lt;p&gt;First things first, we set up a few variables that we use later - we use &lt;code class=&quot;highlighter-rouge&quot;&gt;$PSScriptRoot&lt;/code&gt; if BuildHelpers hasn’t run, set a verbose flag if we see &lt;code class=&quot;highlighter-rouge&quot;&gt;!verbose&lt;/code&gt; in a commit message, and run an Init task that doesn’t do much.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test Phase&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Next up we start running some tests:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-PowerShell&quot;&gt;$TestResults = Invoke-Pester -Path $ProjectRoot\Tests -PassThru -OutputFormat NUnitXml -OutputFile &quot;$ProjectRoot\$TestFile&quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If you want a re-usable header for your *.tests.ps1, you can use the output from BuildHelpers to generalize things:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# [Content from *.tests.ps1, not psake.ps1] #&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$PSVersion&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$PSVersionTable&lt;/span&gt;.PSVersion.Major
&lt;span class=&quot;nv&quot;&gt;$ModuleName&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$ENV&lt;/span&gt;:BHProjectName
&lt;span class=&quot;nv&quot;&gt;$ModulePath&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Join-Path&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$ENV&lt;/span&gt;:BHProjectPath &lt;span class=&quot;nv&quot;&gt;$ModuleName&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Verbose output for non-master builds on appveyor&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# Handy for troubleshooting.&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# Splat @Verbose against commands as needed&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$Verbose&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; @&lt;span class=&quot;o&quot;&gt;{}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$ENV&lt;/span&gt;:BHBranchName -notlike &lt;span class=&quot;s2&quot;&gt;&quot;master&quot;&lt;/span&gt; -or &lt;span class=&quot;nv&quot;&gt;$env&lt;/span&gt;:BHCommitMessage -match &lt;span class=&quot;s2&quot;&gt;&quot;!verbose&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$Verbose&lt;/span&gt;.add&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Verbose&quot;&lt;/span&gt;,&lt;span class=&quot;nv&quot;&gt;$True&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

Import-Module &lt;span class=&quot;nv&quot;&gt;$ModulePath&lt;/span&gt; -Force
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Back in psake.ps1, we check to see if &lt;code class=&quot;highlighter-rouge&quot;&gt;$ENV:BHBuildSystem&lt;/code&gt; is AppVeyor, and push up test results if so.  This isn’t critical, but it gives us &lt;a href=&quot;https://ci.appveyor.com/project/RamblingCookieMonster/psdeploy/build/tests&quot;&gt;a list of test results&lt;/a&gt; on AppVeyor.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Build Phase&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;After the tests run, we can start a build phase.  Not everyone will want to use this, but I like to run two shortcuts:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Bump the module version&lt;/li&gt;
  &lt;li&gt;Update &lt;code class=&quot;highlighter-rouge&quot;&gt;FunctionsToExport = '*'&lt;/code&gt; in the module manifest to include all exported functions&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Load the module, read the exported functions, update the psd1 FunctionsToExport to include exported functions&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;Set&lt;/span&gt;-ModuleFunctions

&lt;span class=&quot;c1&quot;&gt;# Bump the module version&lt;/span&gt;
Update-Metadata -Path &lt;span class=&quot;nv&quot;&gt;$env&lt;/span&gt;:BHPSModuleManifest
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Deploy Phase&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Now for the fun part! We deploy the module:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$Params = @{
    Path = $ProjectRoot
    Force = $true
    Recurse = $false # We keep psdeploy.ps1 test artifacts, avoid deploying those : )
}
Invoke-PSDeploy @Verbose @Params
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That was a bit anti-climactic! We’ve offloaded all the logic for publishing our module to PSDeploy, leaving us cleaner code.&lt;/p&gt;

&lt;h3 id=&quot;deploypsdeployps1&quot;&gt;Deploy.PSDeploy.ps1&lt;/h3&gt;

&lt;p&gt;We have two deployments in here.  If we’re in a recognized build system (&lt;code class=&quot;highlighter-rouge&quot;&gt;$env:BHBuildSystem&lt;/code&gt;), in the master branch (&lt;code class=&quot;highlighter-rouge&quot;&gt;$env:BHBranchName&lt;/code&gt;), and have a commit message that includes !deploy (&lt;code class=&quot;highlighter-rouge&quot;&gt;$env:BHCommitMessage&lt;/code&gt;), we publish our module to the gallery:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Deploy Module &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    By PSGalleryModule &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        FromSource &lt;span class=&quot;nv&quot;&gt;$ENV&lt;/span&gt;:BHPSModulePath
        To PSGallery
        WithOptions @&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            ApiKey &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$ENV&lt;/span&gt;:NugetApiKey
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is great, but wouldn’t it be nice to have development builds that folks could test and try out?  We can borrow from the PowerShell team’s &lt;a href=&quot;https://github.com/PowerShell/DscResources#development-builds&quot;&gt;idea&lt;/a&gt; (and code!) of deploying NuGet packages to AppVeyor and add a second deployment - sounds complicated and fancy, but it’s not too bad:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Deploy DeveloperBuild {
    By AppVeyorModule {
        FromSource $ENV:BHPSModulePath
        To AppVeyor
        WithOptions @{
            Version = $env:APPVEYOR_BUILD_VERSION
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href=&quot;http://psdeploy.readthedocs.io/en/latest/Example-AppVeyorModule-Deployment/&quot;&gt;Hit the docs&lt;/a&gt; for details on how to install a module published to AppVeyor.&lt;/p&gt;

&lt;p&gt;That’s it! We can now install our official module from the PowerShell Gallery, and install development builds from AppVeyor.&lt;/p&gt;

&lt;h2 id=&quot;what-happens-now&quot;&gt;What Happens Now?&lt;/h2&gt;

&lt;p&gt;This whole process sounds complicated, but it’s driven by four generic files you can add or update in your repository:  appveyor.yml, build.ps1, psake.ps1, and something.psdeploy.ps1.&lt;/p&gt;

&lt;p&gt;Here’s what happens &lt;em&gt;automatically&lt;/em&gt; with every commit we push to GitHub:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;GitHub sends AppVeyor a notification of your commit&lt;/li&gt;
  &lt;li&gt;AppVeyor parses your &lt;a href=&quot;https://github.com/RamblingCookieMonster/PSDeploy/blob/f813a8ba39702cf446fe0b23994e18936412ea9c/appveyor.yml&quot;&gt;appveyor.yml&lt;/a&gt; and starts &lt;a href=&quot;https://www.appveyor.com/docs/build-configuration#build-pipeline&quot;&gt;a build&lt;/a&gt; on a fresh VM&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/RamblingCookieMonster/PSDeploy/blob/f813a8ba39702cf446fe0b23994e18936412ea9c/build.ps1&quot;&gt;build.ps1&lt;/a&gt; installs dependencies, sets up environment variables with BuildHelpers, and kicks off psake.ps1&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/RamblingCookieMonster/PSDeploy/blob/ab04c9c4122d520b475711f254fb65e520e212e8/psake.ps1&quot;&gt;psake.ps1&lt;/a&gt; does the real work.  It runs your Pester tests, and if they pass, runs PSDeploy against your &lt;a href=&quot;https://github.com/RamblingCookieMonster/PSDeploy/blob/9b14a0807e1aa52b4df8e41ac5b66de8f63a3f2e/deploy.psdeploy.ps1&quot;&gt;psdeploy.ps1&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s about it! Once this scaffolding is in place, you can let GitHub and AppVeyor do the work for you, and start thinking about applying &lt;a href=&quot;http://aka.ms/thereleasepipelinemodel&quot;&gt;release pipelines&lt;/a&gt; like this to your infrastructure!&lt;/p&gt;

&lt;h2 id=&quot;rambling-outro-the-gallery&quot;&gt;Rambling Outro: The Gallery&lt;/h2&gt;

&lt;p&gt;Today, the PowerShell Gallery has fewer than 1,000 modules, many of them focusing on Azure and DSC.  Hardly comparable to repositories like CPAN, PyPI, or RubyGems:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.modulecounts.com/&quot;&gt;&lt;img src=&quot;/images/deploy/modulecount.png&quot; alt=&quot;Module Counts&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hopefully we can get to a point where more folks are contributing to the gallery.  There are a few things that might move this along:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Using tools like this pipeline could simplify and automate publication, encouraging lazy folks like myself to use the gallery&lt;/li&gt;
  &lt;li&gt;Educating our peers and outdated legal teams on the &lt;a href=&quot;http://www.themacro.com/articles/2016/05/why-the-best-give-away/&quot;&gt;importance of open source&lt;/a&gt; could unlock a trove of internal tools that would be quite valuable to the community.  Chef offers &lt;a href=&quot;https://www.chef.io/blog/event/webinar-open-source-licensing-by-lawyers-for-lawyers/&quot;&gt;a webinar for lawyers&lt;/a&gt;; more education targeted at this common road-block-to-sharing might be helpful&lt;/li&gt;
  &lt;li&gt;Microsoft could lead by example.  Do you see OperationsManager, SqlServer, SqlPs, or Microsoft.SharePoint.Powershell on the gallery?&lt;/li&gt;
  &lt;li&gt;You could pester your vendors to distribute modules on the gallery.  &lt;a href=&quot;http://ramblingcookiemonster.github.io/REST-PowerShell-and-Infoblox/&quot;&gt;More should be writing modules&lt;/a&gt;, and those that do rarely publish them on the gallery.  Props to teams like Amazon’s &lt;a href=&quot;https://www.powershellgallery.com/packages/AWSPowerShell&quot;&gt;who already do this&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Open sourcing PowerShell could open the doors to pragmatic OSS contributors who have no motivation to work with an OS-specific language&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cheers!&lt;/p&gt;

    &lt;p&gt;&lt;a href=&quot;http://ramblingcookiemonster.github.io/PSDeploy-Inception/&quot;&gt;A PowerShell Module Release Pipeline&lt;/a&gt; was originally published by Warren F at &lt;a href=&quot;http://ramblingcookiemonster.github.io&quot;&gt;Rambling Cookie Monster&lt;/a&gt; on July 14, 2016.&lt;/p&gt;
  </content>
</entry>


<entry>
  <title type="html"><![CDATA[PowerShell and Slack]]></title>
  <link rel="alternate" type="text/html" href="http://ramblingcookiemonster.github.io/PSSlack/" />
  <id>http://ramblingcookiemonster.github.io/PSSlack</id>
  <updated>2016-08-30T11:00:00+00:00</updated>
  <published>2016-05-21T11:00:00+00:00</published>
  
  <author>
    <name>Warren F</name>
    <uri>http://ramblingcookiemonster.github.io</uri>
    
  </author>
  <content type="html">
    &lt;section id=&quot;table-of-contents&quot; class=&quot;toc&quot;&gt;
  &lt;header&gt;
    &lt;h3&gt;&lt;i class=&quot;fa fa-book&quot;&gt;&lt;/i&gt; Overview&lt;/h3&gt;
  &lt;/header&gt;
&lt;div id=&quot;drawer&quot;&gt;
&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#rambling&quot; id=&quot;markdown-toc-rambling&quot;&gt;Rambling&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#why-chat&quot; id=&quot;markdown-toc-why-chat&quot;&gt;Why Chat?&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#slack&quot; id=&quot;markdown-toc-slack&quot;&gt;Slack&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#psslack&quot; id=&quot;markdown-toc-psslack&quot;&gt;PSSlack&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#psslack-prerequisites&quot; id=&quot;markdown-toc-psslack-prerequisites&quot;&gt;PSSlack Prerequisites&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#psslack-installation&quot; id=&quot;markdown-toc-psslack-installation&quot;&gt;PSSlack Installation&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#using-psslack&quot; id=&quot;markdown-toc-using-psslack&quot;&gt;Using PSSlack&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#rich-attachments&quot; id=&quot;markdown-toc-rich-attachments&quot;&gt;Rich Attachments&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#multiple-attachments-and-fields&quot; id=&quot;markdown-toc-multiple-attachments-and-fields&quot;&gt;Multiple Attachments and Fields&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#searching-slack&quot; id=&quot;markdown-toc-searching-slack&quot;&gt;Searching Slack&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#getting-history&quot; id=&quot;markdown-toc-getting-history&quot;&gt;Getting History&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#posting-files&quot; id=&quot;markdown-toc-posting-files&quot;&gt;Posting Files&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#next-steps&quot; id=&quot;markdown-toc-next-steps&quot;&gt;Next Steps&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

  &lt;/div&gt;
&lt;/section&gt;
&lt;!-- /#table-of-contents --&gt;

&lt;h2 id=&quot;rambling&quot;&gt;Rambling&lt;/h2&gt;

&lt;p&gt;I grew up using IRC.  Before Napster and Steam, there were other ways to find music and games.  As time went by, I shifted over to more legitimate use, with freenode and other networks offering solid technical resources, including channels for PowerShell, VMware, Citrix, and more.&lt;/p&gt;

&lt;p&gt;At some point, the Slack craze started, and Joel Bennett created the PowerShell Slack team (&lt;a href=&quot;http://slack.poshcode.org/&quot;&gt;invite link&lt;/a&gt;).  It took some convincing, but sometimes I just want a pretty, simple to use solution that works across platforms.  Slack fit the bill, so I hopped on!&lt;/p&gt;

&lt;p&gt;Eventually, I ended up on a team that uses Slack for communication.  Perfect excuse to write a Slack module!&lt;/p&gt;

&lt;h3 id=&quot;why-chat&quot;&gt;Why Chat?&lt;/h3&gt;

&lt;p&gt;So!  Let’s step back a moment and consider why you might want a synchronous group chat system…  Let’s use two example cases.  Which would you prefer:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Case 1&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;A critical issue occurs.  You’re heading to a physical war room.  Not a loud person?  Good luck getting your voice in there.  Love repeating the status?  Perfect, you’ll do this each time someone new steps in the room.  Want a comfy keyboard and monitor?  Ha, enjoy your laptop.  Good luck writing your postmortem from this havoc&lt;/li&gt;
  &lt;li&gt;Something big happens, but doesn’t warrant a war room.  Get ready for 10 different e-mail threads with different recipients and miscommunication galore, and a ticket or two with duplicate but not complete information.  Good luck piecing things together!&lt;/li&gt;
  &lt;li&gt;You want to work remotely.  You’re stuck with e-mail, phone calls, and 1:1 oriented IM with something like Lync.  This isn’t condusive to communication&lt;/li&gt;
  &lt;li&gt;You check a variety of sources.  You have a logging solution, a monitoring solution or three, ad hoc e-mail notifications from scripts that go straight to junk mail, and more!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Case 2&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;A critical issues occurs.  You head to a generic emergency chat room, or maybe create a new one specifically for this issue.  You don’t need to interrupt people to raise an important point.  Anyone who joins in can scroll up and read the full transcript.  You can work from the comfort of wherever you want to work.  When all is said and done, you have a good starting point for a postmortem&lt;/li&gt;
  &lt;li&gt;Something big happens, but doesn’t warrant a war room.  You hop on chat, maybe join a generic war room, and hash things out.  You can mention or invite folks to join, and everyone can see the transcript, rather than try to parse 10 e-mail threads.  You can add the chat transcript to your ticketing system, if that’s your thing&lt;/li&gt;
  &lt;li&gt;You want to work remotely.  If everyone is using chat, you can keep up to date with what everyone is up to, start up discussions, and generally have an easier time communicating with your team, regardless of where you are&lt;/li&gt;
  &lt;li&gt;You check a variety of sources.  But many or most of them funnel into a channel or two that you can view to get a quick read on things.  You can &lt;em&gt;search&lt;/em&gt; these things after the fact&lt;/li&gt;
  &lt;li&gt;Your chat system is likely hosted.  This means when shit hits the fan, you have a medium that isn’t depending on your infrastructure to run (on-prem IRC?  No thanks)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Oh.  And if you go beyond sending messages to your chat system, you can do some crazy fun things with a bot.  Check out this awesome bit from Matt Hodgkins on setting up &lt;a href=&quot;https://hodgkins.io/chatops-on-windows-with-hubot-and-powershell&quot;&gt;Hubot with PowerShell&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So!  Hyperbole (or is it?) aside, consider a group chat system; they can be incredibly helpful.  There are a variety of options out there.  Slack and HipChat are two of the better solutions out there.&lt;/p&gt;

&lt;h3 id=&quot;slack&quot;&gt;Slack&lt;/h3&gt;

&lt;p&gt;Slack is a solid &lt;del&gt;gif library&lt;/del&gt; chat solution.  It has a slick client for most platforms, and &lt;a href=&quot;https://devops-summit.slack.com/apps&quot;&gt;a huge library&lt;/a&gt; of integrations.  That being said, you might run into cases where you just want to send a message to Slack.  Why?&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Maybe the system you want to hook into Slack doesn’t have an integration&lt;/li&gt;
  &lt;li&gt;Maybe the existing integration is unhelpful, and blends into a sea of other messages that looks the same&lt;/li&gt;
  &lt;li&gt;Maybe the existing integration doesn’t offer enough customization&lt;/li&gt;
  &lt;li&gt;Some mix.   SCOM is a great example.  No Slack integration, and even if there were, you could add context to SCOM alerts and send them along to Slack, perhaps in addition to &lt;a href=&quot;https://gallery.technet.microsoft.com/scriptcenter/PowerShell-HTML-Notificatio-e1c5759d&quot;&gt;pretty HTML e-mails&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Wait.  But why would I send my stuff to Slack?  It turns out that this is quite helpful.&lt;/p&gt;

&lt;p&gt;Who wouldn’t want to have informational logs, various levels of alerts, conversation, and other data, all in one spot, search-able from any client, without worrying about it hitting you junk mail, being archived, or being spread across your various logging, monitoring, mail, file systems, event logs, and other sources that usually house this data?  Sure, use those other solutions, but do consider how helpful it is to send this stuff to a chat system.&lt;/p&gt;

&lt;p&gt;Isn’t this a PowerShell post, where’s the PowerShell?&lt;/p&gt;

&lt;h2 id=&quot;psslack&quot;&gt;PSSlack&lt;/h2&gt;

&lt;p&gt;Like any project, I like to look around to see what’s available in the community.  There might be an existing module, or at least some functions that you could contribute to or borrow from.&lt;/p&gt;

&lt;p&gt;I found &lt;a href=&quot;https://github.com/jgigler/Powershell.Slack&quot;&gt;an awesome function or two&lt;/a&gt; from jgigler, and a &lt;a href=&quot;https://github.com/smurawski/Slack&quot;&gt;handy module&lt;/a&gt; from Steven Murawski, but I was looking for a bit more functionality.&lt;/p&gt;

&lt;p&gt;I borrowed some work and ideas from jgigler and Steven, and put together a quick and dirty &lt;a href=&quot;https://github.com/RamblingCookieMonster/PSSlack&quot;&gt;PSSlack module&lt;/a&gt;!  Let’s do a quick walk through.&lt;/p&gt;

&lt;h3 id=&quot;psslack-prerequisites&quot;&gt;PSSlack Prerequisites&lt;/h3&gt;

&lt;p&gt;First things first, we need a way to authorize with Slack.&lt;/p&gt;

&lt;p&gt;You can &lt;a href=&quot;https://api.slack.com/web#authentication&quot;&gt;read more&lt;/a&gt;, but we’re just going to &lt;a href=&quot;https://api.slack.com/docs/oauth-test-tokens&quot;&gt;grab a test token&lt;/a&gt; for this exercise.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Make sure you’ve signed into your Slack team in the browser (not just the app)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://api.slack.com/docs/oauth-test-tokens&quot;&gt;Browse to this page&lt;/a&gt;, create a token, and copy it!  Don’t give this out.  Treat it like your username+password&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you’re just sending messages, you could also &lt;a href=&quot;https://my.slack.com/services/new/incoming-webhook/&quot;&gt;set up an incoming webhook&lt;/a&gt; and use the Uri (which you should also treat like a username+password).&lt;/p&gt;

&lt;h3 id=&quot;psslack-installation&quot;&gt;PSSlack Installation&lt;/h3&gt;

&lt;p&gt;The next part is easy!  We’ll use the &lt;a href=&quot;https://www.powershellgallery.com/&quot;&gt;PowerShell Gallery&lt;/a&gt; to grab the module (works with Windows 10, WMF 5, or a separate MSI installer at the link).  You could also pull it down from GitHub.&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Install-Module PSSlack -Force
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That’s it!  Now you can start exploring the commands and help:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Import the module.&lt;/span&gt;
    Import-Module PSSlack    &lt;span class=&quot;c1&quot;&gt;#Alternatively, Import-Module \\Path\To\PSSlack&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Get commands in the module&lt;/span&gt;
    Get-Command -Module PSSlack

&lt;span class=&quot;c1&quot;&gt;# Get help&lt;/span&gt;
    Get-Help Send-SlackMessage -Full
    Get-Help about_PSSlack
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;using-psslack&quot;&gt;Using PSSlack&lt;/h2&gt;

&lt;p&gt;We’re done with the boring bits, lets look at the actual module!  First things first, you can send a simple message with no bells or whistles:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$token&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'xoxp-some-token'&lt;/span&gt;

Send-SlackMessage -Token &lt;span class=&quot;nv&quot;&gt;$token&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;`&lt;/span&gt;
                  -Channel &lt;span class=&quot;s1&quot;&gt;'@wframe'&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;`&lt;/span&gt;
                  -Parse full &lt;span class=&quot;se&quot;&gt;`&lt;/span&gt;
                  -Text &lt;span class=&quot;s1&quot;&gt;'Hello @wframe, join me in #devnull!'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href=&quot;/images/slack/SimpleMessage.png&quot;&gt;&lt;img src=&quot;/images/slack/SimpleMessage.png&quot; alt=&quot;Simple Message&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s look at some more features.&lt;/p&gt;

&lt;h3 id=&quot;rich-attachments&quot;&gt;Rich Attachments&lt;/h3&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;New-SlackMessageAttachment -Color &lt;span class=&quot;k&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;System.Drawing.Color]::red&lt;span class=&quot;k&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;`&lt;/span&gt;
                           -Title &lt;span class=&quot;s1&quot;&gt;'The System Is Down'&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;`&lt;/span&gt;
                           -TitleLink https://www.youtube.com/watch?v&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;TmpRs7xN06Q &lt;span class=&quot;se&quot;&gt;`&lt;/span&gt;
                           -Text &lt;span class=&quot;s1&quot;&gt;'Please Do The Needful'&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;`&lt;/span&gt;
                           -Pretext &lt;span class=&quot;s1&quot;&gt;'Everything is broken'&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;`&lt;/span&gt;
                           -AuthorName &lt;span class=&quot;s1&quot;&gt;'SCOM Bot'&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;`&lt;/span&gt;
                           -AuthorIcon &lt;span class=&quot;s1&quot;&gt;'http://ramblingcookiemonster.github.io/images/tools/wrench.png'&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;`&lt;/span&gt;
                           -Fallback &lt;span class=&quot;s1&quot;&gt;'Your client is bad'&lt;/span&gt; |
    New-SlackMessage -Channel &lt;span class=&quot;s1&quot;&gt;'devnull'&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;`&lt;/span&gt;
                     -IconEmoji :bomb: |
    Send-SlackMessage -Token &lt;span class=&quot;nv&quot;&gt;$Token&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In this example, we create a &lt;a href=&quot;https://api.slack.com/docs/attachments&quot;&gt;Slack attachment&lt;/a&gt;, a way to provide a richer message.  We pipe this along to New-SlackMessage, which constructs a &lt;a href=&quot;https://api.slack.com/docs/formatting&quot;&gt;Slack message&lt;/a&gt;.  Finally, we send the newly create message with our existing token.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/slack/Attachment.png&quot;&gt;&lt;img src=&quot;/images/slack/Attachment.png&quot; alt=&quot;Rich Attachment&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;multiple-attachments-and-fields&quot;&gt;Multiple Attachments and Fields&lt;/h3&gt;

&lt;p&gt;Let’s throw in a kitchen sink.  We can pipe multiple attachments together, and include a table of fields from any PowerShell object.  We made up a fake example here:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
&lt;span class=&quot;c1&quot;&gt;#You could pipe anything into New-SlackField.  We'll pretend to pull monitoring alert data:&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$Fields&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;pscustomobject]@&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    ImpactedService &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'All The Things'&lt;/span&gt;
    Severity &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; 11
    ImpactedDepartment &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'All'&lt;/span&gt;
    URL &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'https://www.youtube.com/watch?v=TmpRs7xN06Q'&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; | New-SlackField -Short

&lt;span class=&quot;c1&quot;&gt;# Notice that we can chain multiple attachments:&lt;/span&gt;
New-SlackMessageAttachment -Color &lt;span class=&quot;k&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;System.Drawing.Color]::red&lt;span class=&quot;k&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;`&lt;/span&gt;
                           -Title &lt;span class=&quot;s1&quot;&gt;'The System Is Down'&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;`&lt;/span&gt;
                           -TitleLink https://www.youtube.com/watch?v&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;TmpRs7xN06Q &lt;span class=&quot;se&quot;&gt;`&lt;/span&gt;
                           -Text &lt;span class=&quot;s1&quot;&gt;'Please Do The Needful'&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;`&lt;/span&gt;
                           -Pretext &lt;span class=&quot;s1&quot;&gt;'Everything is broken'&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;`&lt;/span&gt;
                           -AuthorName &lt;span class=&quot;s1&quot;&gt;'SCOM Bot'&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;`&lt;/span&gt;
                           -AuthorIcon &lt;span class=&quot;s1&quot;&gt;'http://ramblingcookiemonster.github.io/images/tools/wrench.png'&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;`&lt;/span&gt;
                           -Fallback &lt;span class=&quot;s1&quot;&gt;'Your client is bad'&lt;/span&gt; |
    New-SlackMessageAttachment -Color &lt;span class=&quot;k&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;System.Drawing.Color]::Orange&lt;span class=&quot;k&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;`&lt;/span&gt;
                               -Fields &lt;span class=&quot;nv&quot;&gt;$Fields&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;`&lt;/span&gt;
                               -Fallback &lt;span class=&quot;s1&quot;&gt;'Your client is bad'&lt;/span&gt; |
    New-SlackMessage -Channel &lt;span class=&quot;s1&quot;&gt;'devnull'&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;`&lt;/span&gt;
                     -IconEmoji :bomb: &lt;span class=&quot;se&quot;&gt;`&lt;/span&gt;
                     -AsUser &lt;span class=&quot;se&quot;&gt;`&lt;/span&gt;
                     -Username &lt;span class=&quot;s1&quot;&gt;'SCOM Bot'&lt;/span&gt; |
    Send-SlackMessage -Token &lt;span class=&quot;nv&quot;&gt;$Token&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href=&quot;/images/slack/Fields.png&quot;&gt;&lt;img src=&quot;/images/slack/Fields.png&quot; alt=&quot;Fields&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That’s about it for messages!  Get creative.  A while back we replaced our default, contextless SCOM notifications with a nice &lt;a href=&quot;https://gallery.technet.microsoft.com/scriptcenter/PowerShell-HTML-Notificatio-e1c5759d&quot;&gt;HTML e-mail notification&lt;/a&gt;.  It would be simple to hook the same alerts into Slack, for example.&lt;/p&gt;

&lt;p&gt;What else can we do?&lt;/p&gt;

&lt;h3 id=&quot;searching-slack&quot;&gt;Searching Slack&lt;/h3&gt;

&lt;p&gt;Slack has a great built in search, but maybe you want to hook something up that only sends messages if a UID or search term doesn’t appear in Slack already.&lt;/p&gt;

&lt;p&gt;We’ll look for PowerShell discussion:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Find-SlackMessage -Token &lt;span class=&quot;nv&quot;&gt;$Token&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;`&lt;/span&gt;
                  -Query &lt;span class=&quot;s1&quot;&gt;'PowerShell'&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;`&lt;/span&gt;
                  -SortBy timestamp
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href=&quot;/images/slack/Find.png&quot;&gt;&lt;img src=&quot;/images/slack/Find.png&quot; alt=&quot;Find&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There’s a lot more context if we use Select-Object:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Find-SlackMessage -Token &lt;span class=&quot;nv&quot;&gt;$Token&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;`&lt;/span&gt;
                  -Query &lt;span class=&quot;s1&quot;&gt;'PowerShell'&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;`&lt;/span&gt;
                  -SortBy timestamp |
    &lt;span class=&quot;nb&quot;&gt;Select-Object&lt;/span&gt; -Property &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href=&quot;/images/slack/FindSelect.png&quot;&gt;&lt;img src=&quot;/images/slack/FindSelect.png&quot; alt=&quot;Find Select&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can also do something similar with channel history.&lt;/p&gt;

&lt;h3 id=&quot;getting-history&quot;&gt;Getting History&lt;/h3&gt;

&lt;p&gt;There was recently a call for PowerShell / DevOps related topics.  We’ll use this as a demo for what you might do with history:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Get-SlackChannel -Token &lt;span class=&quot;nv&quot;&gt;$token&lt;/span&gt; -Name &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;topic-requests |
    Get-SlackHistory -Token &lt;span class=&quot;nv&quot;&gt;$token&lt;/span&gt; -Count 1000 |
    &lt;span class=&quot;nb&quot;&gt;Where&lt;/span&gt;-Object &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$_&lt;/span&gt;.Reactions.Count -gt 0&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; |
    &lt;span class=&quot;nb&quot;&gt;Select&lt;/span&gt; @&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;l&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'Thumbs'&lt;/span&gt;; &lt;span class=&quot;nv&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;={&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$_&lt;/span&gt;.Reactions[0].count&lt;span class=&quot;o&quot;&gt;}}&lt;/span&gt;, Text |
    &lt;span class=&quot;nb&quot;&gt;Sort &lt;/span&gt;Thumbs -Descending
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We find the channel in question, look for history, only return messages with reactions, add a thumbs property that contains the count of reactions, and sort:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/slack/History.png&quot;&gt;&lt;img src=&quot;/images/slack/History.png&quot; alt=&quot;History&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;posting-files&quot;&gt;Posting Files&lt;/h3&gt;

&lt;p&gt;This is a work in progress, but here’s an example of uploading a snippet:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$Snippet&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; @&lt;span class=&quot;s1&quot;&gt;'
Send-SlackFile -Token $Token `
               -Channel general `
               -Content $Snippet `
               -FileType powershell `
               -filename example.ps1
'&lt;/span&gt;@

Send-SlackFile -Token &lt;span class=&quot;nv&quot;&gt;$Token&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;`&lt;/span&gt;
               -Channel general &lt;span class=&quot;se&quot;&gt;`&lt;/span&gt;
               -Content &lt;span class=&quot;nv&quot;&gt;$Snippet&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;`&lt;/span&gt;
               -FileType powershell &lt;span class=&quot;se&quot;&gt;`&lt;/span&gt;
               -filename example.ps1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href=&quot;/images/slack/Files.png&quot;&gt;&lt;img src=&quot;/images/slack/Files.png&quot; alt=&quot;Files&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Notice that this sends as me - you would need to register an application and grab a real token if you want this to appear from a bot.&lt;/p&gt;

&lt;p&gt;Thanks to Brooks’ pull request, we now support sending files, because your Slack team could use a few more gifs!&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/slack/psslack.gif&quot;&gt;&lt;img src=&quot;/images/slack/psslack_small.gif&quot; alt=&quot;gif upload&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That’s about it!&lt;/p&gt;

&lt;h2 id=&quot;next-steps&quot;&gt;Next Steps&lt;/h2&gt;

&lt;p&gt;There’s more to come.  Slack has a nice, &lt;a href=&quot;https://api.slack.com/methods&quot;&gt;well documented API&lt;/a&gt;, that offers you a &lt;a href=&quot;https://api.slack.com/methods/channels.history/test&quot;&gt;GUI for simple testing&lt;/a&gt; of each method.  We’re slowly working our way through these methods to see what would be valuable, so be sure to stop back, this module will be changing rapidly.&lt;/p&gt;

&lt;p&gt;Suggestions, pull requests, and other contributions would be more than welcome.  If you have any ideas, check to see if there’s already a request in &lt;a href=&quot;https://github.com/RamblingCookieMonster/PSSlack/issues&quot;&gt;the issues&lt;/a&gt;, and open an issue!&lt;/p&gt;

&lt;p&gt;Cheers!&lt;/p&gt;

    &lt;p&gt;&lt;a href=&quot;http://ramblingcookiemonster.github.io/PSSlack/&quot;&gt;PowerShell and Slack&lt;/a&gt; was originally published by Warren F at &lt;a href=&quot;http://ramblingcookiemonster.github.io&quot;&gt;Rambling Cookie Monster&lt;/a&gt; on May 21, 2016.&lt;/p&gt;
  </content>
</entry>


<entry>
  <title type="html"><![CDATA[PSDeploy&#58; Take Two]]></title>
  <link rel="alternate" type="text/html" href="http://ramblingcookiemonster.github.io/PSDeploy-Take-Two/" />
  <id>http://ramblingcookiemonster.github.io/PSDeploy-Take-Two</id>
  <updated>2016-04-01T09:00:00+00:00</updated>
  <published>2016-04-01T09:00:00+00:00</published>
  
  <author>
    <name>Warren F</name>
    <uri>http://ramblingcookiemonster.github.io</uri>
    
  </author>
  <content type="html">
    &lt;section id=&quot;table-of-contents&quot; class=&quot;toc&quot;&gt;
  &lt;header&gt;
    &lt;h3&gt;&lt;i class=&quot;fa fa-book&quot;&gt;&lt;/i&gt; Overview&lt;/h3&gt;
  &lt;/header&gt;
&lt;div id=&quot;drawer&quot;&gt;
&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#rambling&quot; id=&quot;markdown-toc-rambling&quot;&gt;Rambling&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#why-would-i-use-psdeploy&quot; id=&quot;markdown-toc-why-would-i-use-psdeploy&quot;&gt;Why Would I Use PSDeploy?&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#how-do-i-install-psdeploy&quot; id=&quot;markdown-toc-how-do-i-install-psdeploy&quot;&gt;How Do I Install PSDeploy?&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#how-do-i-use-psdeploy&quot; id=&quot;markdown-toc-how-do-i-use-psdeploy&quot;&gt;How Do I Use PSDeploy&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#a-basic-deployment&quot; id=&quot;markdown-toc-a-basic-deployment&quot;&gt;A basic deployment&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#a-more-fun-deployment&quot; id=&quot;markdown-toc-a-more-fun-deployment&quot;&gt;A more fun deployment&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#invoke-psdeploy&quot; id=&quot;markdown-toc-invoke-psdeploy&quot;&gt;Invoke-PSDeploy&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#what-can-i-deploy&quot; id=&quot;markdown-toc-what-can-i-deploy&quot;&gt;What Can I Deploy?&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#what-changed-in-psdeploy&quot; id=&quot;markdown-toc-what-changed-in-psdeploy&quot;&gt;What Changed in PSDeploy?&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#wrapping-up&quot; id=&quot;markdown-toc-wrapping-up&quot;&gt;Wrapping Up&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

  &lt;/div&gt;
&lt;/section&gt;
&lt;!-- /#table-of-contents --&gt;

&lt;h2 id=&quot;rambling&quot;&gt;Rambling&lt;/h2&gt;

&lt;p&gt;So!  I’m a dad now.  Everything went smoothly, Hannah arrived mid-March, at 8lbs 9oz.  I’m a light sleeper, so I now have a few hours every night to hack away at things in a sleep-deprived state, lending an occasional hand with our trouble-making daughter.&lt;/p&gt;

&lt;p&gt;Right before all the fun, I got a fun message from Michael Greene:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;wanted to ask where you want to go with that PSDeploy project.  could we get it to a DSL along the lines of “Deploy &amp;#60;type&amp;#62; &amp;#60;path&amp;#62; To &amp;#60;Target&amp;#62;” and then from any project just invoke-deployment.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It sounded interesting, but complicated.  I was happy with the simple YAML implementation, but gave the change some thought.  A DSL would mean deployment configurations could call PowerShell inline with the deployment.  It wouldn’t add much complexity for a user, but it would add some serious flexibility.&lt;/p&gt;

&lt;p&gt;This post will be a quick and dirty overview of the revamped PSDeploy.&lt;/p&gt;

&lt;h2 id=&quot;why-would-i-use-psdeploy&quot;&gt;Why Would I Use PSDeploy?&lt;/h2&gt;

&lt;p&gt;Maybe you’re an IT professional, and you’re starting to use important tools like version control.  This can be a pain:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Do you commit to version control and manually copy things out to where they live in the real world?&lt;/li&gt;
  &lt;li&gt;Do you edit things where they sit in the real world, and then commit that to version control?&lt;/li&gt;
  &lt;li&gt;Do you ever let these get out of sync?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A simple answer to this is to borrow an idea that developers have been using for a while:  continuous deployment.&lt;/p&gt;

&lt;p&gt;When you commit to version control, a tool like &lt;a href=&quot;http://jenkins-ci.org/&quot;&gt;Jenkins&lt;/a&gt;, &lt;a href=&quot;https://www.jetbrains.com/teamcity/&quot;&gt;TeamCity&lt;/a&gt;, &lt;a href=&quot;http://www.appveyor.com/&quot;&gt;AppVeyor&lt;/a&gt;, &lt;a href=&quot;http://octopusdeploy.com/&quot;&gt;Octopus Deploy&lt;/a&gt;, or other CI/CD solutions will see this, and can automatically deploy your changes out to production.  Ideally after some tests run successfully.  PSDeploy can help simplify that deployment.&lt;/p&gt;

&lt;p&gt;So!  What does this actually look like for an IT professional?  Here’s a simple example:&lt;/p&gt;

&lt;p&gt;I make a change to a scheduled task script on my computer.  I push those changes to version control, and the rest is automatic!  In the background, Jenkins sees my change, calls PSDeploy, and the scheduled task script is pushed out to production!&lt;/p&gt;

&lt;p&gt;Here’s an illustration from the &lt;a href=&quot;http://ramblingcookiemonster.github.io/PSDeploy/&quot;&gt;original PSDeploy post&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://cloud.githubusercontent.com/assets/6377597/9177949/7c5a0c26-3f62-11e5-9d31-61f74a324383.png&quot;&gt;&lt;img src=&quot;https://cloud.githubusercontent.com/assets/6377597/9177951/7fec1fa0-3f62-11e5-98bc-6a077d1c57f0.png&quot; alt=&quot;psdeployflowsmall&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;how-do-i-install-psdeploy&quot;&gt;How Do I Install PSDeploy?&lt;/h2&gt;

&lt;p&gt;You can pull down PSDeploy a few ways.&lt;/p&gt;

&lt;p&gt;The easy way, assuming you have PowerShell 5, or install the down-level &lt;a href=&quot;https://www.powershellgallery.com/&quot;&gt;PowerShellGet module&lt;/a&gt;:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Install-Module PSDeploy

Import-Module PSDeploy
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The old-fashioned way:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/RamblingCookieMonster/PSDeploy/archive/master.zip&quot;&gt;Download the repository archive&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Unblock the archive file&lt;/li&gt;
  &lt;li&gt;Extract the PSDeploy folder to a module path (e.g. &lt;code class=&quot;highlighter-rouge&quot;&gt;$env:USERPROFILE\Documents\WindowsPowerShell\Modules\&lt;/code&gt;)&lt;/li&gt;
  &lt;li&gt;Import the module: &lt;code class=&quot;highlighter-rouge&quot;&gt;Import-Module PSDeploy&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once you’ve installed PSDeploy, you can check out the various functions and help:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Get commands in the module&lt;/span&gt;
    Get-Command -Module PSDeploy

&lt;span class=&quot;c1&quot;&gt;# Get help for the module and a command&lt;/span&gt;
    Get-Help about_PSDeploy
    Get-Help Invoke-PSDeploy -full
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That’s about it, you’re ready to get started with PowerShell based deployments.&lt;/p&gt;

&lt;h2 id=&quot;how-do-i-use-psdeploy&quot;&gt;How Do I Use PSDeploy&lt;/h2&gt;

&lt;p&gt;So! You’ve installed PSDeploy.  Now what?&lt;/p&gt;

&lt;p&gt;At a high level, you write a *.psdeploy.ps1 deployment script.  PSDeploy reads this and runs the deployments that you defined.&lt;/p&gt;

&lt;p&gt;Let’s step through a few examples to illustrate the basics.  We’ll pretend we have the following files and folders for these examples:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/psdeploy2/sourcefiles.png&quot;&gt;&lt;img src=&quot;/images/psdeploy2/sourcefiles.png&quot; alt=&quot;Source files&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;a-basic-deployment&quot;&gt;A basic deployment&lt;/h3&gt;

&lt;p&gt;At the very least, we need a Deploy, By, FromSource, and To:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Deploy SomeDeploymentName &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    By FileSystem &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        FromSource MyModule
        To C:\PSDeployTo
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You can check what PSDeploy sees by running Get-PSDeployment:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Get-PSDeployment -Path C:\PSDeployFrom\my.psdeploy.ps1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href=&quot;/images/psdeploy2/getpsdeployment.png&quot;&gt;&lt;img src=&quot;/images/psdeploy2/getpsdeployment.png&quot; alt=&quot;Get-PSDeployment&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s add some more features!&lt;/p&gt;

&lt;h3 id=&quot;a-more-fun-deployment&quot;&gt;A more fun deployment&lt;/h3&gt;

&lt;p&gt;We’re going to use a few new features of PSDeploy:  tags and dependencies.  They’re relatively pointless here, but might help you with an actual deployment need:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Deploy SomeDeploymentName &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    By FileSystem AllTheThings &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        FromSource MyModule,
                   SomeScripts
        To C:\PSDeployTo
        DependingOn SomeDeploymentName-MyModule  &lt;span class=&quot;c1&quot;&gt;#DeploymentName-ByName&lt;/span&gt;
        Tagged Dev
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    By FileSystem MyModule &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        FromSource MyModule
        To \\ServerY\c&lt;span class=&quot;nv&quot;&gt;$\&lt;/span&gt;SomePSModulePath,
           \\ServerX\SomeShare&lt;span class=&quot;nv&quot;&gt;$\&lt;/span&gt;Modules
        Tagged Prod,
               Module
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Let’s look at how PSDeploy sees this:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/psdeploy2/getpsdeployment2.png&quot;&gt;&lt;img src=&quot;/images/psdeploy2/getpsdeployment2.png&quot; alt=&quot;Get-PSDeployment&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Like many PowerShell objects, we can find more properties using Select-Object:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/psdeploy2/getpsdeployment2all.png&quot;&gt;&lt;img src=&quot;/images/psdeploy2/getpsdeployment2all.png&quot; alt=&quot;Get-PSDeployment&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So! We see that the deployments have tags, and Get-PSDeployment gave us the results back taking the dependency we specified into account.&lt;/p&gt;

&lt;p&gt;Finally, maybe we only want to look at deployments tagged prod:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/psdeploy2/getpsdeployment2tags.png&quot;&gt;&lt;img src=&quot;/images/psdeploy2/getpsdeployment2tags.png&quot; alt=&quot;Get-PSDeployment&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So, how do we actually run these deployments?&lt;/p&gt;

&lt;h3 id=&quot;invoke-psdeploy&quot;&gt;Invoke-PSDeploy&lt;/h3&gt;

&lt;p&gt;Invoking a deployment is simple:  we call Invoke-PSDeploy.  This borrows a few conventions from Invoke-Pester.&lt;/p&gt;

&lt;p&gt;Running Invoke-PSDeploy with no parameters will recursively search for *.psdeploy.ps1 files under your current path, and run everything that it finds.  In this scenario, any relative paths in the deployment use the current path as the deployment root:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Invoke-PSDeploy
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Running Invoke-PSDeploy with a path parameter specifying a folder will recursively search for *.psdeploy.ps1 files under that specific folder.  It will use the specfied folder as the root for any relative paths in the deployment:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Invoke-PSDeploy -Path \\Path\Below\PSDeployConfigs\
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Finally, you can run Invoke-PSDeploy with a path parameter specifying a *.psdeploy.ps1 file.  It will use that deployment script’s parent path as the root for any relative paths in the deployment:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Invoke-PSDeploy -Path \\Path\To\Some.PSDeploy.ps1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;what-can-i-deploy&quot;&gt;What Can I Deploy?&lt;/h2&gt;

&lt;p&gt;Now that you know how to use PSDeploy, what can you deploy?  Some of these things might seem simple, but having a layer of abstraction to hide some of your code can be quite helpful:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;FileSystem&lt;/strong&gt;:  Copy files or folders.  Uses copy-item and robocopy behind the scenes, respectively&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;FileSystemRemote&lt;/strong&gt;:  Same as FileSystem, but runs in a remote PSSession.  Mind the double hop.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;MkDocs&lt;/strong&gt;:  Build and deploy an &lt;a href=&quot;http://www.mkdocs.org/&quot;&gt;MkDocs&lt;/a&gt; site - thanks to Michael Lombardi!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It’s pretty bare bones for now, but contributions would be more than welcome. &lt;a href=&quot;https://github.com/RamblingCookieMonster/PSDeploy/wiki/Extending-PSDeploy&quot;&gt;Extending PSDeploy&lt;/a&gt; takes two quick steps:  write a script to handle deployments, and a config file that tells PSDeploy what DeploymentType invokes that script.&lt;/p&gt;

&lt;p&gt;It might be a fun way to get experience working with GitHub, and perhaps some integration testing, if you’re up for it!&lt;/p&gt;

&lt;p&gt;As time goes on, you can run &lt;code class=&quot;highlighter-rouge&quot;&gt;Get-PSDeploymentType&lt;/code&gt; to list available deployment types, and &lt;code class=&quot;highlighter-rouge&quot;&gt;Get-PSDeploymentType SomeDeploymentType -ShowHelp&lt;/code&gt; to view the help for that deployment type:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/psdeploy2/getpsdeploymenttype.png&quot;&gt;&lt;img src=&quot;/images/psdeploy2/getpsdeploymenttype.png&quot; alt=&quot;Get-PSDeploymentType&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/psdeploy2/getpsdeploymenttypehelp.png&quot;&gt;&lt;img src=&quot;/images/psdeploy2/getpsdeploymenttypehelp.png&quot; alt=&quot;Get-PSDeployment&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That’s about it! Let’s recap.&lt;/p&gt;

&lt;h2 id=&quot;what-changed-in-psdeploy&quot;&gt;What Changed in PSDeploy?&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;*.PSDeploy.ps1 deployment configurations&lt;/strong&gt;, in addition to yaml&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Yaml deployment configurations deprecated&lt;/strong&gt;.  These still work, but development efforts and new features will focus on *.psdeploy.ps1 scripts&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Tagged deployments&lt;/strong&gt; to help filter or categorize deployments&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Deployment dependencies&lt;/strong&gt; to help ensure deployments perform in the necessary order&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Noop DeploymentType&lt;/strong&gt; to help explore or debug the environment when a deployment runs&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;wrapping-up&quot;&gt;Wrapping Up&lt;/h2&gt;

&lt;p&gt;This was a fun little project!  I’ve found the functionality quite helpful for PowerShell based deployments, whether they start in Jenkins, GitLab CI, or from the shell.&lt;/p&gt;

&lt;p&gt;If you’d like to contribute or collaborate, &lt;a href=&quot;https://github.com/RamblingCookieMonster/PSDeploy&quot;&gt;issues and pull requests&lt;/a&gt; would be welcome!  If you want to extend PSDeploy with more deployment types, some brief notes are available &lt;a href=&quot;https://github.com/RamblingCookieMonster/PSDeploy/wiki/Extending-PSDeploy&quot;&gt;in the wiki&lt;/a&gt;, and I would be more than happy to help!&lt;/p&gt;

&lt;p&gt;Cheers!&lt;/p&gt;

    &lt;p&gt;&lt;a href=&quot;http://ramblingcookiemonster.github.io/PSDeploy-Take-Two/&quot;&gt;PSDeploy&amp;#58; Take Two&lt;/a&gt; was originally published by Warren F at &lt;a href=&quot;http://ramblingcookiemonster.github.io&quot;&gt;Rambling Cookie Monster&lt;/a&gt; on April 01, 2016.&lt;/p&gt;
  </content>
</entry>


<entry>
  <title type="html"><![CDATA[On Learning, Sharing, and My First Tech Job]]></title>
  <link rel="alternate" type="text/html" href="http://ramblingcookiemonster.github.io/Learning-And-Sharing/" />
  <id>http://ramblingcookiemonster.github.io/Learning-And-Sharing</id>
  <updated>2016-02-23T22:00:00+00:00</updated>
  <published>2016-02-23T22:00:00+00:00</published>
  
  <author>
    <name>Warren F</name>
    <uri>http://ramblingcookiemonster.github.io</uri>
    
  </author>
  <content type="html">
    &lt;section id=&quot;table-of-contents&quot; class=&quot;toc&quot;&gt;
  &lt;header&gt;
    &lt;h3&gt;&lt;i class=&quot;fa fa-book&quot;&gt;&lt;/i&gt; Overview&lt;/h3&gt;
  &lt;/header&gt;
&lt;div id=&quot;drawer&quot;&gt;
&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#rambling&quot; id=&quot;markdown-toc-rambling&quot;&gt;Rambling&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#firsttechjob&quot; id=&quot;markdown-toc-firsttechjob&quot;&gt;FirstTechJob&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#learning&quot; id=&quot;markdown-toc-learning&quot;&gt;Learning&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#rambling-1&quot; id=&quot;markdown-toc-rambling-1&quot;&gt;Rambling&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#takeaway&quot; id=&quot;markdown-toc-takeaway&quot;&gt;Takeaway&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#sharing&quot; id=&quot;markdown-toc-sharing&quot;&gt;Sharing&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#rambling-2&quot; id=&quot;markdown-toc-rambling-2&quot;&gt;Rambling&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#takeaway-1&quot; id=&quot;markdown-toc-takeaway-1&quot;&gt;Takeaway&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#closing&quot; id=&quot;markdown-toc-closing&quot;&gt;Closing.&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

  &lt;/div&gt;
&lt;/section&gt;
&lt;!-- /#table-of-contents --&gt;

&lt;h2 id=&quot;rambling&quot;&gt;Rambling&lt;/h2&gt;

&lt;p&gt;Over the weekend, a fun thing happened on Twitter.  Someone encouraged folks to post about their first tech job.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://twitter.com/garethr/status/701099443857309696&quot;&gt;&lt;img src=&quot;/images/learn/firsttechjob.png&quot; alt=&quot;FirstTechJob&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It brought to mind two of my primary motivations: learning, and sharing.&lt;/p&gt;

&lt;h3 id=&quot;firsttechjob&quot;&gt;FirstTechJob&lt;/h3&gt;

&lt;p&gt;I lucked out.  While I’ve always been enthusiastic about computing, when I first applied for a job in IT, my experience consisted of an unrelated undergrad degree (brains!), a co-op in risk and compliance (not very technical), and being a student in a networking and systems administration program at a local university.&lt;/p&gt;

&lt;p&gt;As much as you learn in school, we all know that nothing can make up for actual experience in the real world.  I still have no idea what I’m doing, but I had even less of a clue back then.&lt;/p&gt;

&lt;p&gt;So! I applied for a few jobs.  I was assuming a help desk job would be the only option, probably on an odd shift.&lt;/p&gt;

&lt;p&gt;I didn’t hear back from most places.  Eventually I took a shot in the dark, and applied for a data interfaces job at a local hospital; something I had no business applying for.  I chatted with the hiring manager, the place seemed nice, but I was clearly not the best fit.  Thankfully, they ended up passing my resume along, and I got a random call for a site support position.&lt;/p&gt;

&lt;p&gt;That was it!  I started working with Windows in a domain environment for the first time, helping folks with their laptops, desktops, and other client systems.  Somehow, I managed to (mostly) steer clear of printers.&lt;/p&gt;

&lt;h2 id=&quot;learning&quot;&gt;Learning&lt;/h2&gt;

&lt;h3 id=&quot;rambling-1&quot;&gt;Rambling&lt;/h3&gt;

&lt;p&gt;Learning is so important in the world of technology.  If you don’t enjoy learning, you’re going to hate working in IT.  Or you’ll be &lt;em&gt;that guy/girl&lt;/em&gt;, the one who &lt;a href=&quot;http://ramblingcookiemonster.github.io/Dealing-With-The-Click-Next-Admin/&quot;&gt;happily resorts to clicking next&lt;/a&gt;, no matter how much encouragement and support you give them to learn more and help themselves and their team.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://twitter.com/jepayneMSFT/status/701136556338184192&quot;&gt;&lt;img src=&quot;/images/learn/noteveryone.png&quot; alt=&quot;Not everyone&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I started out as &lt;em&gt;that guy&lt;/em&gt;, more out of ignorance than intention.  I tried to eek out my co-workers’ years of wisdom.  I learned about important technologies at each job along the way.  When I moved up to a third level support role, I learned more about servers and networking, and got a superficial introduction to Active Directory, VMware, and at the suggestion of a mentor, VBscript.&lt;/p&gt;

&lt;p&gt;That last bit panned out.  I moved into a systems engineer role immediately after the exposure to VBscript, probably way before I was ready.  So I dove in!  VBscript was a pain, but the &lt;a href=&quot;https://www.penflip.com/powershellorg/why-powershell/blob/master/chapter2.txt&quot;&gt;value of scripting&lt;/a&gt; was clear: thankfully PowerShell was already well established!&lt;/p&gt;

&lt;p&gt;Rather than picking a narrow field to specialize in, I decided to focus on something that applies across IT: scripting! I started &lt;a href=&quot;http://ramblingcookiemonster.github.io/How-Do-I-Learn-PowerShell/&quot;&gt;learning PowerShell&lt;/a&gt;, and continue to learn more to this day.  One of the many &lt;a href=&quot;https://www.penflip.com/powershellorg/why-powershell/blob/master/chapter3.txt&quot;&gt;benefits to PowerShell&lt;/a&gt; is that you can use it to glue together pretty much any technology.  I used this to learn more, and to get involved in interesting projects.  SQL Server, Infoblox, Isilon, vCenter, Active Directory, SCOM, you name it.&lt;/p&gt;

&lt;p&gt;I’m following the same pattern today, and am starting to to learn *nix toolsets and basics, Python, Ruby, Puppet, Salt, and more.  The yaks are lining up.&lt;/p&gt;

&lt;h3 id=&quot;takeaway&quot;&gt;Takeaway&lt;/h3&gt;

&lt;p&gt;These helped me out.  Take this with a grain of salt, but I suspect they may help you as well:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Be curious&lt;/strong&gt;.  Explore.  If you don’t enjoy learning, steer clear of IT&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;em&gt;It’s not my job&lt;/em&gt; is a terrible excuse&lt;/strong&gt;.  Always be open to learning topics outside of your current role&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Find and learn force-multipliers that apply across most jobs in IT&lt;/strong&gt;.  Scripting (e.g. PowerShell), data manipulation (e.g. SQL), and others like these are valuable skills&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;You don’t need to specialize&lt;/strong&gt;.  Knowing a broad range of topics at shallow to intermediate depth, and a few at extensive depth can be quite valuable&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;sharing&quot;&gt;Sharing&lt;/h2&gt;

&lt;h3 id=&quot;rambling-2&quot;&gt;Rambling&lt;/h3&gt;

&lt;p&gt;Learning and sharing go hand in hand.  Many of the resources I found most helpful were shared by other IT professionals: code snippets, functions, tools, libraries, ideas, questions and answers, you name it.&lt;/p&gt;

&lt;p&gt;So!  I tried to contribute back.&lt;/p&gt;

&lt;p&gt;I started out by just sharing tips, tricks, and &lt;a href=&quot;http://ramblingcookiemonster.github.io/Pages/Tools/index.html&quot;&gt;tools&lt;/a&gt; with co-workers.  Eventually I picked up PowerShell, and started sharing snippets, and modules (think of them like toolboxes) with a bunch of handy functions (tools) oriented around our environment.&lt;/p&gt;

&lt;p&gt;I enjoyed sharing with co-workers, so I took it to the next stage, and started a blog.  Nothing too exciting, I mostly shared tech news and ideas to start.  A first look at PowerShell Web Access in the wild.  Details on the awesome new PowerShell 3.  A &lt;a href=&quot;http://ramblingcookiemonster.github.io/Pages/PowerShellResources/index.html#cheat-sheets-and-quick-references&quot;&gt;PowerShell Cheat Sheet&lt;/a&gt;.  A few months after starting, I released my first function (a fork of Shay Levy’s code), &lt;a href=&quot;https://gallery.technet.microsoft.com/scriptcenter/Get-NetworkStatistics-66057d71&quot;&gt;Get-NetworkStatistics&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I’ve been sharing ever since.  If a function or module is general purpose, doesn’t include business logic, and is easily sanitized, I publish it.  I didn’t explicitly ask my boss, although the topic came up a few times.  I’d rather ask for forgiveness than permission, but you might have a tighter risk appetite, or work for a more litigious employer.&lt;/p&gt;

&lt;p&gt;I find that topic irksome.  Employers benefit from more informed IT practitioners.  Preventing IT folks from sharing their work holds back both the employer and IT practitioners who could have benefited from the knowledge.  It also creates some uncertainty, where the more risk-averse among us avoid sharing, even if they aren’t under contractual obligation to hoard knowledge.&lt;/p&gt;

&lt;p&gt;We all benefit from sharing.  Rather than having every IT professional write duplicate tools, with no feedback or contributions from others, it makes more sense to share our code and ideas.  Less duplication of effort, more eyes on the code or ideas, more feedback from folks taking your ideas or code and using them in new and different ways; this benefits the practice of IT itself.  Heck, it might not be the intent, but if you read the LOPSA code of ethics, they mention how important sharing is:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://lopsa.org/CodeOfEthics&quot;&gt;&lt;img src=&quot;/images/learn/lopsa.png&quot; alt=&quot;LOPSA Education&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;takeaway-1&quot;&gt;Takeaway&lt;/h3&gt;

&lt;p&gt;Again, I found these beneficial, but take them with a grain of salt:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Share.  &lt;strong&gt;You’ll learn more&lt;/strong&gt;.  You might polish your code and follow practices you would otherwise ignore.  You’ll probably want to review information and learn more about the topic at hand before you post about it&lt;/li&gt;
  &lt;li&gt;Share.  &lt;strong&gt;You might get helpful feedback&lt;/strong&gt; and end up with a better solution&lt;/li&gt;
  &lt;li&gt;Share.  &lt;strong&gt;You’ll help others in IT&lt;/strong&gt;, who in turn might share their own ideas and code&lt;/li&gt;
  &lt;li&gt;Share.  &lt;strong&gt;It looks good to many employers&lt;/strong&gt;.  All else being equal, if I’m looking at two candidates, and only one of them has code and ideas open to the public, they’ll probably get the nod&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Don’t rule out sharing off-hand&lt;/strong&gt;.  Consider the risk and reward of sharing your sanitized code and ideas&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Provide digestible details&lt;/strong&gt;.  What’s the goal of your code?  How do we install it?  How do we use it?  Readme.md files or short blog posts go a long way.  As silly as it sounds, animated gifs are a great way to quickly get across an idea.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;closing&quot;&gt;Closing.&lt;/h2&gt;

&lt;p&gt;That’s about it!&lt;/p&gt;

&lt;p&gt;I owe a debt of gratitude to my co-workers, and the various PowerShell and other IT communities and contributors out there.  If they hadn’t shared their knowledge, I wouldn’t have had the same resources to learn with, and wouldn’t be able to share back the little that I can.&lt;/p&gt;

&lt;p&gt;Don’t be afraid of learning something new, and share what you’re learning along the way.  I went from “what’s PowerShell?” to “&lt;a href=&quot;https://www.penflip.com/powershellorg/why-powershell/blob/master/chapter1.txt&quot;&gt;Why PowerShell?&lt;/a&gt;.” in a few short years.  Years sounds like a long time, but spending a little time every day was all it took.&lt;/p&gt;

&lt;p&gt;Cheers!  Hopefully back to posting more technical content soon.&lt;/p&gt;

    &lt;p&gt;&lt;a href=&quot;http://ramblingcookiemonster.github.io/Learning-And-Sharing/&quot;&gt;On Learning, Sharing, and My First Tech Job&lt;/a&gt; was originally published by Warren F at &lt;a href=&quot;http://ramblingcookiemonster.github.io&quot;&gt;Rambling Cookie Monster&lt;/a&gt; on February 23, 2016.&lt;/p&gt;
  </content>
</entry>


<entry>
  <title type="html"><![CDATA[PowerShell Is Too Hard]]></title>
  <link rel="alternate" type="text/html" href="http://ramblingcookiemonster.github.io/PowerShell-Is-Too-Hard/" />
  <id>http://ramblingcookiemonster.github.io/PowerShell-Is-Too-Hard</id>
  <updated>2016-02-18T22:00:00+00:00</updated>
  <published>2016-02-18T22:00:00+00:00</published>
  
  <author>
    <name>Warren F</name>
    <uri>http://ramblingcookiemonster.github.io</uri>
    
  </author>
  <content type="html">
    &lt;section id=&quot;table-of-contents&quot; class=&quot;toc&quot;&gt;
  &lt;header&gt;
    &lt;h3&gt;&lt;i class=&quot;fa fa-book&quot;&gt;&lt;/i&gt; Overview&lt;/h3&gt;
  &lt;/header&gt;
&lt;div id=&quot;drawer&quot;&gt;
&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#rambling&quot; id=&quot;markdown-toc-rambling&quot;&gt;Rambling&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#the-task&quot; id=&quot;markdown-toc-the-task&quot;&gt;The Task&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#read-a-file-in-c&quot; id=&quot;markdown-toc-read-a-file-in-c&quot;&gt;Read a file in C&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#read-a-file-in-c-1&quot; id=&quot;markdown-toc-read-a-file-in-c-1&quot;&gt;Read a file in C#&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#read-a-file-in-python&quot; id=&quot;markdown-toc-read-a-file-in-python&quot;&gt;Read a file in Python&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#read-a-file-in-powershell&quot; id=&quot;markdown-toc-read-a-file-in-powershell&quot;&gt;Read a file in PowerShell&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#you-dont-need-to-be-a-developer-to-use-powershell&quot; id=&quot;markdown-toc-you-dont-need-to-be-a-developer-to-use-powershell&quot;&gt;You Don’t Need to be a Developer to use PowerShell&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#how-do-i-get-started&quot; id=&quot;markdown-toc-how-do-i-get-started&quot;&gt;How do I get started?&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

  &lt;/div&gt;
&lt;/section&gt;
&lt;!-- /#table-of-contents --&gt;

&lt;h2 id=&quot;rambling&quot;&gt;Rambling&lt;/h2&gt;

&lt;p&gt;I lucked out and no longer need to convince co-workers or their managers that it’s in their best interest to learn how to write simple code.  Unfortunately, in most enterprise environments, a &lt;a href=&quot;http://ramblingcookiemonster.github.io/Dealing-With-The-Click-Next-Admin/&quot;&gt;significant proportion&lt;/a&gt; of the IT workforce seem to enjoy square wheels.&lt;/p&gt;

&lt;p&gt;How many times have you seen this?&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/hard/toobusy.png&quot;&gt;&lt;img src=&quot;/images/hard/toobusy.png&quot; alt=&quot;I don't have time to learn&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Some common excuses include &lt;em&gt;it’s too hard&lt;/em&gt;, &lt;em&gt;I’m too busy&lt;/em&gt;, or &lt;em&gt;I’m not a developer!&lt;/em&gt; Often with context around how busy they are working on manual tasks that could be automated, or simplified with a small script.&lt;/p&gt;

&lt;p&gt;I won’t tell you that going from no experience to a decent scripter is something you learn overnight, but you need to start somewhere.&lt;/p&gt;

&lt;p&gt;PowerShell is a tool that most of us can handle learning, and would benefit from greatly, even right out of the gate. Lower level languages are great for developers, but they might require a more significant investment in time, without as much value in return (for an IT professional).&lt;/p&gt;

&lt;p&gt;Let’s illustrate this difference and highlight how helpful PowerShell is, by looking at a simple task across four popular languages.&lt;/p&gt;

&lt;h2 id=&quot;the-task&quot;&gt;The Task&lt;/h2&gt;

&lt;p&gt;We’re going to learn how to read a file.  Exciting, right?  We’ll do this using the following languages:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;PowerShell&lt;/strong&gt;, a task-oriented scripting and shell language. Work in a Microsoft ecosystem? &lt;a href=&quot;https://www.penflip.com/powershellorg/why-powershell/blob/master/chapter1.txt&quot;&gt;PowerShell is probably the best way to get things done&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Python&lt;/strong&gt;, a high-level general purpose programming language&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;C#&lt;/strong&gt;, a feature-full general purpose programming language&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;C&lt;/strong&gt;, a low-level language that terrifies me&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is roughly in order of how much hand holding a language will do for you, which many IT professionals don’t realize.  Let’s start from the ground up.  Keep in mind there are many ways to skin a cat in each of these languages.&lt;/p&gt;

&lt;h3 id=&quot;read-a-file-in-c&quot;&gt;Read a file in C&lt;/h3&gt;

&lt;p&gt;So! First, we need to get a compiler (Visual Studio is your best bet).  Then, we search around for a simple solution.  Protip: don’t write in C if you can help it. You need to do everything on your own, which invariably means whatever you write will be insecure.&lt;/p&gt;

&lt;p&gt;Here’s a “simple” program to read C:\file.txt:&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/6170da1a25751fe3b111.js&quot;&gt; &lt;/script&gt;

&lt;p&gt;&lt;a href=&quot;/images/hard/c.png&quot;&gt;&lt;img src=&quot;/images/hard/c.png&quot; alt=&quot;C&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And voila, it worked!&lt;/p&gt;

&lt;p&gt;Good luck querying LDAP, pulling info from VMware vCenter, or hitting a web API using C. Also, get ready for fun if you’re not very familiar with C and need to read or debug someone else’s code.&lt;/p&gt;

&lt;p&gt;Let’s move up to a higher level language, C#.&lt;/p&gt;

&lt;h3 id=&quot;read-a-file-in-c-1&quot;&gt;Read a file in C#&lt;/h3&gt;

&lt;p&gt;Next up, C#.  You might have Visual Studio installed already; if not you can compile a simple C# file with csc.exe: e.g. &lt;code class=&quot;highlighter-rouge&quot;&gt;C:\Windows\Microsoft.NET\Framework\v4.0.30319\csc.exe YourFile.cs&lt;/code&gt;&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/6c3ae82a321b792b7cca.js&quot;&gt; &lt;/script&gt;

&lt;p&gt;&lt;a href=&quot;/images/hard/cs.png&quot;&gt;&lt;img src=&quot;/images/hard/cs.png&quot; alt=&quot;C#&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Okay! Now we’re getting somewhere. Even if I couldn’t write that on my own, I can get a feel for what’s going on by reading it. But, it’s still a bit heavy on the developer side:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;You need to compile&lt;/li&gt;
  &lt;li&gt;You can’t forget a semi-colon&lt;/li&gt;
  &lt;li&gt;You’re not just saying “read-a-file”, you have to use .NET classes to tell C# how to read a file. Now do this for every task in your solution. This is why I find comments like &lt;em&gt;just use C# instead of PowerShell&lt;/em&gt; quite absurd&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you’re not a developer, it’s important to note how helpful that last bullet is. While C# isn’t a task-oriented language, it uses the .NET Framework, which provides a vast and incredibly valuable library of code.&lt;/p&gt;

&lt;p&gt;If you’re an IT professional trying to automate a technology where the vendor got lazy, there’s a good chance you can write a PowerShell function or module that wraps their .NET library (the caveats &lt;a href=&quot;http://ramblingcookiemonster.github.io/REST-PowerShell-and-Infoblox/&quot;&gt;here&lt;/a&gt; still apply: vendors should really provide a PowerShell module for their customers).&lt;/p&gt;

&lt;p&gt;Enough rambling, let’s move into the world of scripting, and look at reading a file in Python.&lt;/p&gt;

&lt;h3 id=&quot;read-a-file-in-python&quot;&gt;Read a file in Python&lt;/h3&gt;

&lt;p&gt;So! Being in the Microsoft ecosystem, PowerShell is often the top choice. If you plan to step outside that ecosystem, or just want the experience (protip: this is very valuable), Python is a solid, approachable choice.&lt;/p&gt;

&lt;p&gt;We’re going to cut our line count quite a bit:&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/bacdf9bd418f5960fe0f.js&quot;&gt; &lt;/script&gt;

&lt;p&gt;&lt;a href=&quot;/images/hard/python.png&quot;&gt;&lt;img src=&quot;/images/hard/python.png&quot; alt=&quot;Python&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Nice! So that was much shorter, and the result is even easier to read than C#. Python is a high level language often used for scripting, but it’s not as abstracted out and task-based as PowerShell.&lt;/p&gt;

&lt;h3 id=&quot;read-a-file-in-powershell&quot;&gt;Read a file in PowerShell&lt;/h3&gt;

&lt;p&gt;We wrap up with the simplest solution:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-PowerShell&quot;&gt;Get-Content C:\file.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;a href=&quot;/images/hard/powershell.png&quot;&gt;&lt;img src=&quot;/images/hard/powershell.png&quot; alt=&quot;PowerShell&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It all boils down to a single, easy to read command.  If your co-workers complain that they aren’t developers, ask them if they find commands like the one above too difficult.  Hopefully they are open to learning, and you can help get them started.  Otherwise, what are they doing in IT?&lt;/p&gt;

&lt;p&gt;A developer could hop between any of the languages above and more, for a variety of tasks, often with significant expertise in one or two of their favorites. Learning the basics of a task-based language is not &lt;em&gt;too hard,&lt;/em&gt; and now that nearly everything has an API, &lt;a href=&quot;http://everythingsysadmin.com/2014/02/do-system-administrators-need-.html&quot;&gt;warnings like this&lt;/a&gt; will be more than just wishful thinking.&lt;/p&gt;

&lt;p&gt;So! What’s the moral of this story anyways?&lt;/p&gt;

&lt;h2 id=&quot;you-dont-need-to-be-a-developer-to-use-powershell&quot;&gt;You Don’t Need to be a Developer to use PowerShell&lt;/h2&gt;

&lt;p&gt;Anyone in a Microsoft ecosystem should pick up the basics (or beyond) of PowerShell.  If you can give another person instructions - maybe read a recipe, or describe how to troubleshoot an iPhone - you already have the basic ingredients of coding.  The rest is just syntax, which is a quick Google search away, or even provided to you with &lt;a href=&quot;https://blogs.technet.microsoft.com/heyscriptingguy/2014/01/25/using-powershell-ise-snippets-to-remember-tricky-syntax/&quot;&gt;snippets&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In fact, PowerShell is a prime candidate for scripting and automation &lt;a href=&quot;http://ramblingcookiemonster.github.io/PowerShell-Beyond-Administration/&quot;&gt;outside of the world of IT&lt;/a&gt;.  How would we get there?&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;An open source and cross-platform implementation of the .NET Framework (check) and PowerShell (still waiting)&lt;/li&gt;
  &lt;li&gt;Microsoft pushing their own product groups to provide an interface through PowerShell, for both administration and working with products (SQL is a prime example), and beyond their Server and Tools organization&lt;/li&gt;
  &lt;li&gt;Microsoft pushing and assisting third parties in developing PowerShell modules to cover a wider variety of technologies&lt;/li&gt;
  &lt;li&gt;Microsoft and the community adopting and pushing the PowerShell Gallery - ~560 modules compared to CPAN, PyPI, or RubyGems is sad, regardless of the head start these have&lt;/li&gt;
  &lt;li&gt;Luck : )&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;how-do-i-get-started&quot;&gt;How do I get started?&lt;/h3&gt;

&lt;p&gt;So!  Until then… Are you focused on desktops / clients, printers, identity and access management, databases, virtualization, servers, security, communications, support, networking, or any other pursuit in a Microsoft ecosystem?  You should probably learn the basics of PowerShell.&lt;/p&gt;

&lt;p&gt;References abound, I always recommend &lt;a href=&quot;http://ramblingcookiemonster.github.io/How-Do-I-Learn-PowerShell/&quot;&gt;a three pronged approach&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://ramblingcookiemonster.github.io/How-Do-I-Learn-PowerShell/#formal-resources&quot;&gt;Pick up a formal resource&lt;/a&gt;. Maybe a book you read over a month of lunches.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://ramblingcookiemonster.github.io/How-Do-I-Learn-PowerShell/#join-the-community&quot;&gt;Join the community&lt;/a&gt;. Learn from existing tools and scripts. Ask questions and listen in on &lt;a href=&quot;https://www.reddit.com/r/PowerShell/comments/2gm75d/what_are_some_of_your_favorite_powershell_blogs/ckkfqel&quot;&gt;Twitter&lt;/a&gt;, &lt;a href=&quot;http://slack.poshcode.org/&quot;&gt;Slack&lt;/a&gt;, and &lt;a href=&quot;http://powershell.org/&quot;&gt;PowerShell.org&lt;/a&gt;. Stop by a local PowerShell user group. Contribute to all of these if you can!&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://ramblingcookiemonster.github.io/How-Do-I-Learn-PowerShell/#spend-some-time-with-powershell&quot;&gt;Use PowerShell every day&lt;/a&gt;.  Even if only for a few minutes.  You can’t learn this in one shot.  You won’t learn this if you don’t use it regularly.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Hope to see you around the community!&lt;/p&gt;

    &lt;p&gt;&lt;a href=&quot;http://ramblingcookiemonster.github.io/PowerShell-Is-Too-Hard/&quot;&gt;PowerShell Is Too Hard&lt;/a&gt; was originally published by Warren F at &lt;a href=&quot;http://ramblingcookiemonster.github.io&quot;&gt;Rambling Cookie Monster&lt;/a&gt; on February 18, 2016.&lt;/p&gt;
  </content>
</entry>


<entry>
  <title type="html"><![CDATA[Cross Platform PowerShell Remoting]]></title>
  <link rel="alternate" type="text/html" href="http://ramblingcookiemonster.github.io/Cross-Platform-PowerShell-Remoting/" />
  <id>http://ramblingcookiemonster.github.io/Cross-Platform-PowerShell-Remoting</id>
  <updated>2016-02-06T22:00:00+00:00</updated>
  <published>2016-02-06T22:00:00+00:00</published>
  
  <author>
    <name>Warren F</name>
    <uri>http://ramblingcookiemonster.github.io</uri>
    
  </author>
  <content type="html">
    &lt;section id=&quot;table-of-contents&quot; class=&quot;toc&quot;&gt;
  &lt;header&gt;
    &lt;h3&gt;&lt;i class=&quot;fa fa-book&quot;&gt;&lt;/i&gt; Overview&lt;/h3&gt;
  &lt;/header&gt;
&lt;div id=&quot;drawer&quot;&gt;
&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#rambling&quot; id=&quot;markdown-toc-rambling&quot;&gt;Rambling&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#the-options&quot; id=&quot;markdown-toc-the-options&quot;&gt;The Options&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#ssh&quot; id=&quot;markdown-toc-ssh&quot;&gt;SSH&lt;/a&gt;        &lt;ul&gt;
          &lt;li&gt;&lt;a href=&quot;#getting-started-with-ssh-on-windows&quot; id=&quot;markdown-toc-getting-started-with-ssh-on-windows&quot;&gt;Getting Started with SSH on Windows&lt;/a&gt;&lt;/li&gt;
          &lt;li&gt;&lt;a href=&quot;#running-powershell-from-ssh&quot; id=&quot;markdown-toc-running-powershell-from-ssh&quot;&gt;Running PowerShell from SSH&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#winrb--winrm&quot; id=&quot;markdown-toc-winrb--winrm&quot;&gt;WinRb / WinRM&lt;/a&gt;        &lt;ul&gt;
          &lt;li&gt;&lt;a href=&quot;#using-winrm-from-ruby&quot; id=&quot;markdown-toc-using-winrm-from-ruby&quot;&gt;Using WinRM From Ruby&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#which-should-i-use&quot; id=&quot;markdown-toc-which-should-i-use&quot;&gt;Which Should I Use?&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#whats-on-the-horizon&quot; id=&quot;markdown-toc-whats-on-the-horizon&quot;&gt;What’s on the Horizon?&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

  &lt;/div&gt;
&lt;/section&gt;
&lt;!-- /#table-of-contents --&gt;

&lt;h1 id=&quot;rambling&quot;&gt;Rambling&lt;/h1&gt;

&lt;p&gt;Wow! Four months since my last post.  New city, new home, new job, new tiny human in less than a month, and thankfully, same Wegmans.&lt;/p&gt;

&lt;p&gt;I lucked out on the job front.  Found a fun team full of talented folks using modern tools, with a role focusing on Microsoft and related tech, in a predominantly *nix / OSS oriented environment.  Plenty of room to learn and explore!&lt;/p&gt;

&lt;p&gt;Of course, with everyone being well-versed in languages like bash and Python, and with most infrastructure residing on *nix systems using tools like Puppet for configuration, learning PowerShell hasn’t made it to their to-do list.  Yet.  Hopefully that changes - from my perspective, nothing comes close to PowerShell as an approachable, task-oriented glue language - imagine if it were open sourced and ported to other systems?&lt;/p&gt;

&lt;p&gt;Until then, rather than miss out on the goodness PowerShell enables, we need to figure out an approach to invoke PowerShell from cross platform systems!&lt;/p&gt;

&lt;h2 id=&quot;the-options&quot;&gt;The Options&lt;/h2&gt;

&lt;p&gt;There are a number of approaches, each with benefits and drawbacks.  At a high level, we could use…&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Remoting solutions like SSH or WinRM&lt;/li&gt;
  &lt;li&gt;API oriented solutions like &lt;a href=&quot;https://github.com/toenuff/flancy&quot;&gt;flancy&lt;/a&gt;, or something heavier like ASP.NET Web API&lt;/li&gt;
  &lt;li&gt;Web based solutions, from tools like rundeck, to shoe-horning &lt;a href=&quot;https://hodgkins.io/automating-with-jenkins-and-powershell-on-windows-part-1&quot;&gt;Jenkins&lt;/a&gt;, to &lt;a href=&quot;https://github.com/vmware/webcommander&quot;&gt;WebCommander&lt;/a&gt;, to building your own ASP.NET / C# page&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The other options are absolutely worth exploring, but a remoting solution would be very flexible with minimal overhead.  So, how can we use WinRM or SSH to call PowerShell from non-Windows systems?&lt;/p&gt;

&lt;h2 id=&quot;ssh&quot;&gt;SSH&lt;/h2&gt;

&lt;p&gt;Wait, an SSH server on Windows? Do you really want to rely on some third party or outdated open source option?&lt;/p&gt;

&lt;p&gt;Turns out Microsoft is &lt;a href=&quot;https://blogs.msdn.microsoft.com/powershell/2015/06/03/looking-forward-microsoft-support-for-secure-shell-ssh/&quot;&gt;planning&lt;/a&gt; to deliver an SSH client &lt;em&gt;and server&lt;/em&gt; for Windows systems, and that there is already a usable (ish) &lt;a href=&quot;https://github.com/PowerShell/Win32-OpenSSH&quot;&gt;early preview&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Given that it’s so early, you should definitely consider testing this out and &lt;a href=&quot;https://github.com/PowerShell/Win32-OpenSSH/issues&quot;&gt;contributing feedback&lt;/a&gt;.  You might help shape design decisions and improve the solution for you, and everyone else using it.&lt;/p&gt;

&lt;h3 id=&quot;getting-started-with-ssh-on-windows&quot;&gt;Getting Started with SSH on Windows&lt;/h3&gt;

&lt;p&gt;This is the easy part! Just follow &lt;a href=&quot;https://github.com/PowerShell/Win32-OpenSSH/wiki/Install-Win32-OpenSSH&quot;&gt;the instructions here&lt;/a&gt;.  At some point, this will hopefully receive CI/CD treatment to deliver stable and dev releases, perhaps to a package manager we can hit from &lt;a href=&quot;https://github.com/OneGet/oneget&quot;&gt;OneGet&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you’re reading this in early 2016, be sure to &lt;a href=&quot;https://github.com/PowerShell/Win32-OpenSSH/releases&quot;&gt;grab the right architecture&lt;/a&gt;; the link in the instructions will point you to the 32-bit release.&lt;/p&gt;

&lt;h3 id=&quot;running-powershell-from-ssh&quot;&gt;Running PowerShell from SSH&lt;/h3&gt;

&lt;p&gt;I ran into no issues getting up and running &lt;em&gt;with non-interactive commands&lt;/em&gt;.  Let’s look at a few examples:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Run ipconfig&lt;/span&gt;
ssh &lt;span class=&quot;nt&quot;&gt;-l&lt;/span&gt; wframe@my.domain ts1 ipconfig
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href=&quot;/images/ssh/ipconfig.png&quot;&gt;&lt;img src=&quot;/images/ssh/ipconfig.png&quot; alt=&quot;SSH ipconfig&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# PowerShell isn't the default shell. Let's try it out.&lt;/span&gt;
ssh &lt;span class=&quot;nt&quot;&gt;-l&lt;/span&gt; wframe@my.domain ts1 powershell.exe &lt;span class=&quot;nt&quot;&gt;-command&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;get-service sshd&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href=&quot;/images/ssh/ssh-get-service.png&quot;&gt;&lt;img src=&quot;/images/ssh/ssh-get-service.png&quot; alt=&quot;SSH service&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Double hop issue? Nope!&lt;/span&gt;
ssh &lt;span class=&quot;nt&quot;&gt;-l&lt;/span&gt; wframe@my.domain ts1 powershell.exe &lt;span class=&quot;nt&quot;&gt;-command&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;get-aduser wframe&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href=&quot;/images/ssh/ssh-aduser.png&quot;&gt;&lt;img src=&quot;/images/ssh/ssh-aduser.png&quot; alt=&quot;SSH aduser&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Nice! So, SSHD is working, we can run PowerShell.exe, and we don’t have to worry about the double-hop issue that you might be familiar with on the PowerShell remoting / Kerberos side!&lt;/p&gt;

&lt;p&gt;How about something a little longer, with variables?&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ssh &lt;span class=&quot;nt&quot;&gt;-l&lt;/span&gt; wframe@rc.domain ts1 powershell.exe &lt;span class=&quot;nt&quot;&gt;-noprofile&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-command&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;

    &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$Service&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; = 'SSHD'
    Get-Service &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$Service&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;
&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href=&quot;/images/ssh/ssh-escape-char-fail.png&quot;&gt;&lt;img src=&quot;/images/ssh/ssh-escape-char-fail.png&quot; alt=&quot;SSH escape char fail&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here’s where we run into our first issue.  When calling PowerShell from bash or another interpreter, you need to be mindful of all the interpreters.  In our case, bash helpfully replaced $service for us.  After bash, presumably cmd.exe needs to be accounted for (i.e. we’re not dropped right into PowerShell), and then PowerShell itself.  Maybe you have a snippet that needs double quotes: have fun!&lt;/p&gt;

&lt;p&gt;Ideally, you manage Windows systems from Windows systems that are already running PowerShell, and avoid the mess, but you can’t always make that call.&lt;/p&gt;

&lt;p&gt;In this example, we can simply escape the $:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ssh &lt;span class=&quot;nt&quot;&gt;-l&lt;/span&gt; wframe@rc.domain ts1 powershell.exe &lt;span class=&quot;nt&quot;&gt;-noprofile&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-command&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;

    &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\$&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Service = 'SSHD'
    Get-Service &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\$&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Service
&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href=&quot;/images/ssh/ssh-escape-char.png&quot;&gt;&lt;img src=&quot;/images/ssh/ssh-escape-char.png&quot; alt=&quot;SSH escape char&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, you could always abstract this into a shell script, perhaps using expect to handle the password, but ideally we could wrap this in Python or Ruby.  I had no luck getting this to work in &lt;a href=&quot;https://github.com/paramiko/paramiko&quot;&gt;Paramiko&lt;/a&gt; or it’s abstraction, &lt;a href=&quot;https://pypi.python.org/pypi/spur&quot;&gt;spur&lt;/a&gt;, but feel free to give it a try, and be sure to let others know if you have any luck!&lt;/p&gt;

&lt;p&gt;That’s about it - let’s move on and look at using WinRM.&lt;/p&gt;

&lt;h2 id=&quot;winrb--winrm&quot;&gt;WinRb / WinRM&lt;/h2&gt;

&lt;p&gt;There are a number of ways to invoke PowerShell over WinRM from other languages.  For many, the Python and Ruby modules will likely be your first stop.&lt;/p&gt;

&lt;p&gt;We’re going to skip the Python pywinrm module, given that it requires either plaintext auth, Kerberos, or fun with SSL.  If you already use Kerberos or have a PKI infrastructure in place, you could safely use this.  Otherwise, you’ll find a number of guides for &lt;a href=&quot;https://github.com/diyan/pywinrm&quot;&gt;pywinrm&lt;/a&gt;, &lt;a href=&quot;https://github.com/WinRb/WinRM&quot;&gt;WinRb&lt;/a&gt;, and more, instructing you to configure WinRM to enable basic auth and AllowUnencrypted. &lt;em&gt;Don’t&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;We’ll use the Ruby module for WinRM.  Install Ruby on your system, &lt;code class=&quot;highlighter-rouge&quot;&gt;gem install -r winrm&lt;/code&gt;, and away we go!&lt;/p&gt;

&lt;h3 id=&quot;using-winrm-from-ruby&quot;&gt;Using WinRM From Ruby&lt;/h3&gt;

&lt;p&gt;We’ll assume you already have have PowerShell remoting enabled and accessible from another Windows system.  Thanks to Dan Wanek and Matt Wrock, we can now use &lt;a href=&quot;http://www.hurryupandwait.io/blog/sane-authenticationencryption-arrives-to-ruby-based-cross-platform-winrm-remote-execution&quot;&gt;NTLM authentication&lt;/a&gt;, which doesn’t come with the dependencies or complexities of Kerberos or a PKI.&lt;/p&gt;

&lt;p&gt;Security note: Do consider using SSL and/or a more modern authentication mechanism like Kerberos, but for quick and dirty testing, NTLM is a far safer bet than basic auth with AllowUnencrypted.  Would love to hear some real security folks weigh in on the current method…&lt;/p&gt;

&lt;p&gt;Let’s get started!&lt;/p&gt;

&lt;p&gt;Here’s the gist of what I might run.  More examples abound, just keep in mind we’re using create_executor’s run_powershell_script method, not the &lt;a href=&quot;https://github.com/WinRb/WinRM#deprecated-methods&quot;&gt;deprecated alternatives&lt;/a&gt;.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;#!/usr/local/bin/ruby&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'winrm'&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;winrm&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;WinRM&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;WinRMWebService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'http://ts1:5985/wsman'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                                   &lt;span class=&quot;ss&quot;&gt;:negotiate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                                   &lt;span class=&quot;ss&quot;&gt;:user&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'wframe'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                                   &lt;span class=&quot;ss&quot;&gt;:pass&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'my password'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;winrm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;create_executor&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;executor&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;executor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;run_powershell_script&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;get-service 'sshd'&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stdout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stderr&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;STDOUT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stdout&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;STDERR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stderr&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href=&quot;/images/ssh/winrm-simple.png&quot;&gt;&lt;img src=&quot;/images/ssh/winrm-simple.png&quot; alt=&quot;WinRM service&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is my first time touching Ruby outside of some sparing use with the GitHub pages setup behind this blog, but I figured it would be worth trying to abstract this into a simple, re-usable function.  Cobbled &lt;a href=&quot;https://gist.github.com/RamblingCookieMonster/a1645b534fed02b4b368&quot;&gt;this&lt;/a&gt; together from various stackoverflow queries;  feedback or tips welcome.&lt;/p&gt;

&lt;p&gt;Let’s kick the tires a bit:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Double hop issue? Yep!&lt;/span&gt;
ps-winrm.rb &lt;span class=&quot;nt&quot;&gt;-u&lt;/span&gt; wframe &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;get-aduser wframe&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href=&quot;/images/ssh/winrm-aduser-fail.png&quot;&gt;&lt;img src=&quot;/images/ssh/winrm-aduser-fail.png&quot; alt=&quot;WinRM aduser fail&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The error message is a bit garbled, but you can get the gist of it.  While the underlying cause differs, we run into the double hop issue PowerShell remoting typically gives us.&lt;/p&gt;

&lt;p&gt;Given that NTLM support was just implemented, and that a cursory search doesn’t turn up anything on CredSSP, it looks like the usual CredSSP workaround won’t help.&lt;/p&gt;

&lt;p&gt;Security note: CredSSP will make a red team happy. Do consider &lt;a href=&quot;http://www.powershellmagazine.com/2014/03/06/accidental-sabotage-beware-of-credssp/&quot;&gt;the security implications&lt;/a&gt;, as well as the fact that if you’re using RDP, you’re already using CredSSP.&lt;/p&gt;

&lt;p&gt;Another workaround that works here would be to live dangerously and hit a domain controller directly:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/images/ssh/winrm-aduser.png&quot;&gt;&lt;img src=&quot;/images/ssh/winrm-aduser-dc.png&quot; alt=&quot;WinRM aduser&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That’s about it! I have seen some odd issues with commands that use runspaces, but most every-day use cases seem to work.&lt;/p&gt;

&lt;h2 id=&quot;which-should-i-use&quot;&gt;Which Should I Use?&lt;/h2&gt;

&lt;p&gt;Both options have their strengths and weaknesses, this is really up to you.  I personally prefer to use languages like Ruby or Python over bash, and SSHD is quite new, so I’ll likely lean towards WinRM in the interim. Once I can get Paramiko or spur working against Microsoft’s SSHD, I’ll likely switch over to that.&lt;/p&gt;

&lt;p&gt;Looking ahead, SSH on Windows seems very promising, and should enable simpler integration with cross platform solutions.&lt;/p&gt;

&lt;p&gt;The WinRM implementations are worth exploring and will continue improving, but will come with some limitations, and don’t seem to be receiving contributions from Microsoft (yet).&lt;/p&gt;

&lt;h2 id=&quot;whats-on-the-horizon&quot;&gt;What’s on the Horizon?&lt;/h2&gt;

&lt;p&gt;A nice side-effect of Microsoft using GitHub is that we get a glimpse of what’s coming down the road.&lt;/p&gt;

&lt;p&gt;Check out the Win32-OpenSSH &lt;a href=&quot;https://github.com/PowerShell/Win32-OpenSSH/issues/&quot;&gt;issues&lt;/a&gt; - you’ll see some notes on current limitations and bugs, along with some interesting ideas and plans.  Curious to see how they’ll implement key based authentication for domain accounts (&lt;a href=&quot;https://github.com/PowerShell/Win32-OpenSSH/issues/39&quot;&gt;issue 39&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Be sure to kick the wheels of Microsoft’s SSHD implementation.  You can actually contribute, whether you submit a pull request, a thoughtful bug report, or an idea for a feature.  It’s still quite early, so if you get in your ideas now, who knows, you might help improve this for everyone!&lt;/p&gt;

&lt;p&gt;Cheers!&lt;/p&gt;

    &lt;p&gt;&lt;a href=&quot;http://ramblingcookiemonster.github.io/Cross-Platform-PowerShell-Remoting/&quot;&gt;Cross Platform PowerShell Remoting&lt;/a&gt; was originally published by Warren F at &lt;a href=&quot;http://ramblingcookiemonster.github.io&quot;&gt;Rambling Cookie Monster&lt;/a&gt; on February 06, 2016.&lt;/p&gt;
  </content>
</entry>


<entry>
  <title type="html"><![CDATA[Finding Evil LDAP Queries]]></title>
  <link rel="alternate" type="text/html" href="http://ramblingcookiemonster.github.io/Evil-LDAP-Queries/" />
  <id>http://ramblingcookiemonster.github.io/Evil-LDAP-Queries</id>
  <updated>2015-10-02T22:00:00+00:00</updated>
  <published>2015-10-02T22:00:00+00:00</published>
  
  <author>
    <name>Warren F</name>
    <uri>http://ramblingcookiemonster.github.io</uri>
    
  </author>
  <content type="html">
    &lt;section id=&quot;table-of-contents&quot; class=&quot;toc&quot;&gt;
  &lt;header&gt;
    &lt;h3&gt;&lt;i class=&quot;fa fa-book&quot;&gt;&lt;/i&gt; Overview&lt;/h3&gt;
  &lt;/header&gt;
&lt;div id=&quot;drawer&quot;&gt;
&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#rambling&quot; id=&quot;markdown-toc-rambling&quot;&gt;Rambling&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#the-fun-begins&quot; id=&quot;markdown-toc-the-fun-begins&quot;&gt;The Fun Begins&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#finding-expensive-inefficient-or-long-running-ldap-queries&quot; id=&quot;markdown-toc-finding-expensive-inefficient-or-long-running-ldap-queries&quot;&gt;Finding Expensive, Inefficient, or Long Running LDAP Queries&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#prerequisites&quot; id=&quot;markdown-toc-prerequisites&quot;&gt;Prerequisites&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#the-registry&quot; id=&quot;markdown-toc-the-registry&quot;&gt;The Registry&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#collecting-the-results&quot; id=&quot;markdown-toc-collecting-the-results&quot;&gt;Collecting the Results&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#parsing-and-interpreting-the-results&quot; id=&quot;markdown-toc-parsing-and-interpreting-the-results&quot;&gt;Parsing and Interpreting the Results&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#powershell-modules-write-them&quot; id=&quot;markdown-toc-powershell-modules-write-them&quot;&gt;PowerShell Modules: Write Them&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#get-on-with-it&quot; id=&quot;markdown-toc-get-on-with-it&quot;&gt;Get On With It!&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

  &lt;/div&gt;
&lt;/section&gt;
&lt;!-- /#table-of-contents --&gt;

&lt;h1 id=&quot;rambling&quot;&gt;Rambling&lt;/h1&gt;

&lt;p&gt;A while back, I noticed an intriguing tweet from Mark Morowczynski at Microsoft:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/ldap/tweet.png&quot; alt=&quot;tweet&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Long story short, Mark explains how to search for and analyze &lt;a href=&quot;http://blogs.technet.com/b/askpfeplat/archive/2015/05/11/how-to-find-expensive-inefficient-and-long-running-ldap-queries-in-active-directory.aspx&quot;&gt;expensive, inefficient, and long running LDAP queries in Active Directory&lt;/a&gt;. Bookmarked.&lt;/p&gt;

&lt;p&gt;This week, Mark’s article came in quite handy.&lt;/p&gt;

&lt;h1 id=&quot;the-fun-begins&quot;&gt;The Fun Begins&lt;/h1&gt;

&lt;p&gt;Wednesday morning, someone asked about some SCOM alerts for AD. We see these every so often, and I started explaining that sites with one or two domain controllers lead to somewhat common and often innocuous AD Site Performance Health Degraded alerts.&lt;/p&gt;

&lt;p&gt;I don’t like assumptions though. Interesting. All the domain controllers in one site are at 100% CPU, with crazy CPU queues. This wasn’t a site getting disconnected, or a one-off evil LDAP query. Looks like we get to dive into Mark’s post!&lt;/p&gt;

&lt;h1 id=&quot;finding-expensive-inefficient-or-long-running-ldap-queries&quot;&gt;Finding Expensive, Inefficient, or Long Running LDAP Queries&lt;/h1&gt;

&lt;h2 id=&quot;prerequisites&quot;&gt;Prerequisites&lt;/h2&gt;

&lt;p&gt;Okay! I know I saw ‘PowerShell’ when skimming the article, this should be quick and easy, or so I thought.&lt;/p&gt;

&lt;p&gt;The final stages of our 2012 R2 roll out are scheduled for next week, so we need to &lt;a href=&quot;https://support.microsoft.com/en-us/kb/2800945/en-us&quot;&gt;install KB2800945&lt;/a&gt; on any remaining 2008 R2 domain controllers, and reboot.&lt;/p&gt;

&lt;p&gt;AD is somewhat important, and a juicy target - if you have a tight ship and this is a rare scenario, consider opening a ticket with Microsoft and triggering your (security) incident response process here.&lt;/p&gt;

&lt;h2 id=&quot;the-registry&quot;&gt;The Registry&lt;/h2&gt;

&lt;p&gt;Once our prerequisites are in place, we need to flip a few values in the registry.&lt;/p&gt;

&lt;p&gt;Oh. There’s a screen shot of the registry in the blog post. And a table with registry keys and values. Guess we’ll write &lt;a href=&quot;https://github.com/RamblingCookieMonster/PSLDAPQueryLogging/blob/master/PSLDAPQueryLogging/Public/Enable-LDAPQueryLogging.ps1&quot;&gt;a PowerShell function&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;I’m all for quickly fighting fires with a GUI, but I realize I don’t want to deal with enabling and disabling this manually on several machines. Writing a quick POC function will be far faster. Particularly given that this is a simple registry change, and that Shay Levy has written the fantastic &lt;a href=&quot;https://psremoteregistry.codeplex.com/&quot;&gt;PSRemoteRegistry&lt;/a&gt;, which I no longer consider a dependency. Just say no to providers that don’t let you hit remote systems.&lt;/p&gt;

&lt;p&gt;All we do here is enable logging, and set a few parameters that Mark suggested.&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;Set&lt;/span&gt;-RegDWord -ComputerName &lt;span class=&quot;nv&quot;&gt;$Computer&lt;/span&gt; -Hive LocalMachine -Key &lt;span class=&quot;s1&quot;&gt;'System\CurrentControlSet\Services\NTDS\Diagnostics'&lt;/span&gt; -Value &lt;span class=&quot;s1&quot;&gt;'15 Field Engineering'&lt;/span&gt; -data 5
&lt;span class=&quot;nb&quot;&gt;Set&lt;/span&gt;-RegDWord -ComputerName &lt;span class=&quot;nv&quot;&gt;$Computer&lt;/span&gt; -Hive LocalMachine -Key &lt;span class=&quot;s1&quot;&gt;'SYSTEM\CurrentControlSet\Services\NTDS\Parameters'&lt;/span&gt; -Value &lt;span class=&quot;s1&quot;&gt;'Expensive Search Results Threshold'&lt;/span&gt; -data 0
&lt;span class=&quot;nb&quot;&gt;Set&lt;/span&gt;-RegDWord -ComputerName &lt;span class=&quot;nv&quot;&gt;$Computer&lt;/span&gt; -Hive LocalMachine -Key &lt;span class=&quot;s1&quot;&gt;'SYSTEM\CurrentControlSet\Services\NTDS\Parameters'&lt;/span&gt; -Value &lt;span class=&quot;s1&quot;&gt;'Inefficient Search Results Threshold'&lt;/span&gt; -data 0
&lt;span class=&quot;nb&quot;&gt;Set&lt;/span&gt;-RegDWord -ComputerName &lt;span class=&quot;nv&quot;&gt;$Computer&lt;/span&gt; -Hive LocalMachine -Key &lt;span class=&quot;s1&quot;&gt;'SYSTEM\CurrentControlSet\Services\NTDS\Parameters'&lt;/span&gt; -Value &lt;span class=&quot;s1&quot;&gt;'Search Time Threshold (msecs)'&lt;/span&gt; -data 100
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That’s it! My domain controllers are now logging event 1644, with details on each LDAP query that meets the thresholds I just set; in this case, anything taking over 100ms. Time for coffee.&lt;/p&gt;

&lt;h2 id=&quot;collecting-the-results&quot;&gt;Collecting the Results&lt;/h2&gt;

&lt;p&gt;This could be outdated or flat out ignorant knowledge, but I recall wevtutil epl being incredibly fast, as compared to reading events using Get-WinEvent (or Get-EventLog, eek!). I’m a fan of using tools that meet your needs. In this case, quickly exporting an evtx is a simple one liner.&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Invoke-Command -ComputerName &lt;span class=&quot;nv&quot;&gt;$Computer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;wevtutil epl &lt;span class=&quot;s1&quot;&gt;'Directory Service'&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;\\&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$Using&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:Computer\c&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$\$ENV&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:ComputerName-Evil.evtx&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Alrighty, the events are there. Oh. Be sure your Directory Services event log is large enough to catch enough data. Maybe &lt;a href=&quot;https://github.com/PowerShell/xWinEventLog&quot;&gt;use DSC&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;At this point, I &lt;a href=&quot;https://github.com/RamblingCookieMonster/PSLDAPQueryLogging/blob/master/PSLDAPQueryLogging/Public/Disable-LDAPQueryLogging.ps1&quot;&gt;reverse&lt;/a&gt; the registry changes I made, and get ready for some fun with Excel!&lt;/p&gt;

&lt;h2 id=&quot;parsing-and-interpreting-the-results&quot;&gt;Parsing and Interpreting the Results&lt;/h2&gt;

&lt;p&gt;Mark links to &lt;a href=&quot;https://gallery.technet.microsoft.com/scriptcenter/Event-1644-reader-Export-45205268&quot;&gt;a script&lt;/a&gt; that helps read Event 1644 data. This is quite handy, and it’s awesome that folks are trying to use PowerShell, but the methodology leaves a bit to be desired.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Install Excel, so that you can access the COM interface. &lt;a href=&quot;http://ramblingcookiemonster.github.io/PSExcel-Intro/&quot;&gt;Sigh&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;Read the script and Get-Help. No parameters. Sigh.&lt;/li&gt;
  &lt;li&gt;Run the script, follow the Read-Host guide. Sigh.&lt;/li&gt;
  &lt;li&gt;If you’re like many folks out there, you might get a few errors. My output was still usable.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At this point, you can browse around the results in Excel, &lt;a href=&quot;http://blogs.technet.com/b/askpfeplat/archive/2015/05/11/how-to-find-expensive-inefficient-and-long-running-ldap-queries-in-active-directory.aspx#Analyzing&quot;&gt; as Mark explains in his post&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In our case, the TopTime-IP section came in handy. A single system is making a huge portion of these queries, including a number of 8+ second queries using UID, which isn’t indexed. I gleefully brandish my pitchfork and run a reverse lookup.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/ldap/wat.png&quot; alt=&quot;Wat&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Hopefully this pointed you in the right direction or right at the culprit. Depending on what you do or don’t find, there are a variety of helpful resources:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://blogs.technet.com/b/askpfeplat/archive/2012/11/11/mcm-active-directory-indexing-for-the-masses.aspx&quot;&gt;Indexing&lt;/a&gt;. Active Directory sits on a database. Like most database engines, your domain controllers will be much, much happier if queries use an indexed attribute.&lt;/li&gt;
  &lt;li&gt;Developers, vendors, and sysadmins might not be familiar with how evil the queries they write can be. &lt;a href=&quot;https://msdn.microsoft.com/en-us/library/ms808539.aspx&quot;&gt;Long story&lt;/a&gt; short? Be as specific as possible. Start as deep as you can, set your search scope as tight as possible, and follow efficient query practices like using indexed attributes, returning only the attributes you need, and keeping your wild cards at the end of a string, not the start.&lt;/li&gt;
  &lt;li&gt;Other tools, like the &lt;a href=&quot;http://blogs.technet.com/b/askds/archive/2010/06/08/son-of-spa-ad-data-collector-sets-in-win2008-and-beyond.aspx&quot;&gt;AD data collector sets&lt;/a&gt;, &lt;a href=&quot;http://blogs.technet.com/b/yongrhee/archive/2014/02/01/tool-server-performance-advisor-spa-v3-1-to-troubleshoot-high-cpu-in-lsass-in-ad-domain-controllers.aspx&quot;&gt;SPA&lt;/a&gt;, WireShark, or &lt;a href=&quot;https://technet.microsoft.com/en-us/library/jj714801.aspx&quot;&gt;Microsoft Message Analyzer&lt;/a&gt; might come in handy.&lt;/li&gt;
  &lt;li&gt;We’re assuming you ruled out other common causes, like non-LSASS offenders, mis-sized domain controllers, and other scenarios. Google and MSFT resources are your friend : )&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now, what about next time?&lt;/p&gt;

&lt;h1 id=&quot;powershell-modules-write-them&quot;&gt;PowerShell Modules: Write Them&lt;/h1&gt;

&lt;p&gt;&lt;a href=&quot;http://powershell.org/wp/2015/08/16/abstraction-and-configuration-data/&quot;&gt;Abstraction is important&lt;/a&gt;. Don’t think in terms of “writing scripts”, try to think in terms of writing re-usable tools: functions. Once you’re comfy writing these tools, you need a toolbox to put them in: modules.&lt;/p&gt;

&lt;p&gt;Want to forget about tools and toolboxes to an extent? &lt;a href=&quot;https://channel9.msdn.com/Series/Getting-Started-with-PowerShell-Desired-State-Configuration-DSC&quot;&gt;Desired State Configuration&lt;/a&gt; and configuration management solutions that layer on top might be up your alley. But I digress, and it’s important to note that configuration management isn’t a panacea; Do you plan to change your configurations for a class of system just to enable diagnostics on a handful of instances? Nope.&lt;/p&gt;

&lt;p&gt;I used a quick &lt;a href=&quot;http://ramblingcookiemonster.github.io/Building-A-PowerShell-Module/&quot;&gt;recipe for building a PowerShell module&lt;/a&gt;, published the obnoxiously-named &lt;a href=&quot;https://github.com/RamblingCookieMonster/PSLDAPQueryLogging&quot;&gt;PSLDAPQueryLogging&lt;/a&gt; module to GitHub and the PowerShell Gallery, and I’m ready for next time!&lt;/p&gt;

&lt;p&gt;Here’s a quick snippet where I enable logging on all domain controllers, pull back the logs, and disable logging:&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/f4322853e049f2ec85c6.js&quot;&gt; &lt;/script&gt;

&lt;h1 id=&quot;get-on-with-it&quot;&gt;Get On With It!&lt;/h1&gt;

&lt;p&gt;Be sure to &lt;a href=&quot;http://powershell.org/wp/2015/09/06/writing-and-publishing-powershell-modules/&quot;&gt;share your modules&lt;/a&gt; on GitHub and the &lt;a href=&quot;https://www.powershellgallery.com/pages/GettingStarted&quot;&gt;PowerShell Gallery&lt;/a&gt;. Languages like &lt;a href=&quot;https://www.perl.org/about/whitepapers/perl-cpan.html&quot;&gt;Perl&lt;/a&gt;, &lt;a href=&quot;https://pypi.python.org/pypi&quot;&gt;Python&lt;/a&gt;, and &lt;a href=&quot;https://rubygems.org/&quot;&gt;Ruby&lt;/a&gt; all have repositories like this, each with thousands and thousands of modules. We  have 321. Let’s get to work!&lt;/p&gt;

&lt;p&gt;Barring the occasional &lt;em&gt;I have to share it!&lt;/em&gt; discoveries, this will likely be my last post for some time; life is about to get crazy at home. In a good way. Cheers!&lt;/p&gt;

    &lt;p&gt;&lt;a href=&quot;http://ramblingcookiemonster.github.io/Evil-LDAP-Queries/&quot;&gt;Finding Evil LDAP Queries&lt;/a&gt; was originally published by Warren F at &lt;a href=&quot;http://ramblingcookiemonster.github.io&quot;&gt;Rambling Cookie Monster&lt;/a&gt; on October 02, 2015.&lt;/p&gt;
  </content>
</entry>


<entry>
  <title type="html"><![CDATA[Building a PowerShell Module]]></title>
  <link rel="alternate" type="text/html" href="http://ramblingcookiemonster.github.io/Building-A-PowerShell-Module/" />
  <id>http://ramblingcookiemonster.github.io/Building-A-PowerShell-Module</id>
  <updated>2016-07-24T05:30:00+00:00</updated>
  <published>2015-09-06T22:00:00+00:00</published>
  
  <author>
    <name>Warren F</name>
    <uri>http://ramblingcookiemonster.github.io</uri>
    
  </author>
  <content type="html">
    &lt;section id=&quot;table-of-contents&quot; class=&quot;toc&quot;&gt;
  &lt;header&gt;
    &lt;h3&gt;&lt;i class=&quot;fa fa-book&quot;&gt;&lt;/i&gt; Overview&lt;/h3&gt;
  &lt;/header&gt;
&lt;div id=&quot;drawer&quot;&gt;
&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#rambling&quot; id=&quot;markdown-toc-rambling&quot;&gt;Rambling&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#why-modules&quot; id=&quot;markdown-toc-why-modules&quot;&gt;Why Modules?&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#this-seems-complicated&quot; id=&quot;markdown-toc-this-seems-complicated&quot;&gt;This Seems Complicated!&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#the-ingredients&quot; id=&quot;markdown-toc-the-ingredients&quot;&gt;The Ingredients&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#the-recipe&quot; id=&quot;markdown-toc-the-recipe&quot;&gt;The Recipe&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#following-the-recipe&quot; id=&quot;markdown-toc-following-the-recipe&quot;&gt;Following the Recipe&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#create-a-github-repository&quot; id=&quot;markdown-toc-create-a-github-repository&quot;&gt;Create a GitHub Repository.&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#create-the-module-and-scaffolding-around-it&quot; id=&quot;markdown-toc-create-the-module-and-scaffolding-around-it&quot;&gt;Create the Module and Scaffolding Around It&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#hook-up-appveyor-and-publish-the-module&quot; id=&quot;markdown-toc-hook-up-appveyor-and-publish-the-module&quot;&gt;Hook up AppVeyor and Publish the Module&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#how-i-write-modules-summarized&quot; id=&quot;markdown-toc-how-i-write-modules-summarized&quot;&gt;How I Write Modules, Summarized&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#psstackexchange&quot; id=&quot;markdown-toc-psstackexchange&quot;&gt;PSStackExchange&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#wrapping-up&quot; id=&quot;markdown-toc-wrapping-up&quot;&gt;Wrapping Up&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#side-note-for-vendors&quot; id=&quot;markdown-toc-side-note-for-vendors&quot;&gt;Side Note for Vendors&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

  &lt;/div&gt;
&lt;/section&gt;
&lt;!-- /#table-of-contents --&gt;

&lt;h3 id=&quot;rambling&quot;&gt;Rambling&lt;/h3&gt;

&lt;p&gt;A while back, someone mentioned it might be fun to document the PowerShell-module-writing-process. This has been done before, but I figure it would be fun to post my own process, from initial idea through publication on the official PowerShellGallery.com site.&lt;/p&gt;

&lt;p&gt;I recently discussed the &lt;a href=&quot;http://powershell.org/wp/2015/08/30/2015-august-scripting-games-wrap-up/&quot;&gt;August Scripting Games puzzle&lt;/a&gt; on PowerShell.org, which involved querying a web API. It turns out this is a very common need, and many of the modules we write abstract out these APIs into handy PowerShell functions and modules.&lt;/p&gt;

&lt;p&gt;We’re going to make the assumption you know what a module is, and that you have some experience writing PowerShell functions. If not, be sure to spend some time &lt;a href=&quot;http://ramblingcookiemonster.github.io/How-Do-I-Learn-PowerShell/&quot;&gt;learning PowerShell&lt;/a&gt; before continuing here!&lt;/p&gt;

&lt;p&gt;This post will cover my typical formula for writing a module, using the Stack Exchange API as an example. Feel free to &lt;a href=&quot;https://github.com/RamblingCookieMonster/PSStackExchange/commit/db1277453374cb16684b35cf93a8f5c97288c41f&quot;&gt;browse the PSStackExchange code&lt;/a&gt; on your own.&lt;/p&gt;

&lt;h3 id=&quot;why-modules&quot;&gt;Why Modules?&lt;/h3&gt;

&lt;p&gt;Advanced functions will take you far with PowerShell. If you aren’t writing functions today, be sure to start encapsulating your code in these re-usable tools. But… they have their limits. Here are a few reasons you might bundle your advanced functions in a module:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Simplify code organization&lt;/li&gt;
  &lt;li&gt;Group related functions together&lt;/li&gt;
  &lt;li&gt;Share state between functions, but not with the user&lt;/li&gt;
  &lt;li&gt;Re-use “helper functions” that you don’t want exposed to the user&lt;/li&gt;
  &lt;li&gt;Improve discoverability: &lt;code class=&quot;highlighter-rouge&quot;&gt;Find-Module MyModule&lt;/code&gt; or &lt;code class=&quot;highlighter-rouge&quot;&gt;Get-Command -Module MyModule&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Simplify distribution: &lt;code class=&quot;highlighter-rouge&quot;&gt;Install-Module MyModule&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In our example, we will organize a set of Stack Exchange functions into one module.&lt;/p&gt;

&lt;h3 id=&quot;this-seems-complicated&quot;&gt;This Seems Complicated!&lt;/h3&gt;

&lt;p&gt;Doing this from scratch might take you a little time. Thankfully, once you write a module or two, you can quickly get started by copying it and tweaking a few files. Don’t be scared off by the length of this post; writing your own modules is well worth spending a few minutes to pick up the basics!&lt;/p&gt;

&lt;h3 id=&quot;the-ingredients&quot;&gt;The Ingredients&lt;/h3&gt;

&lt;p&gt;There are many ways to create a module, from slapping a .psm1 extension onto a file, to compiling a fully fledged binary module &lt;a href=&quot;http://www.powershellmagazine.com/2014/03/18/writing-a-powershell-module-in-c-part-1-the-basics/&quot;&gt;from C#&lt;/a&gt;. We’ll take a common middle ground here, and use the following ingredients:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://msdn.microsoft.com/en-us/library/dd878297&quot;&gt;A Module Manifest&lt;/a&gt;&lt;/strong&gt;. This is a .psd1 file that describes your module. &lt;a href=&quot;https://github.com/RamblingCookieMonster/PSStackExchange/blob/db1277453374cb16684b35cf93a8f5c97288c41f/PSStackExchange/PSStackExchange.psd1&quot;&gt;PSStackExchange.psd1&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;A Root Module&lt;/strong&gt;. In our case, a script module .psm1 file. This is just PowerShell code to run when importing the module. &lt;a href=&quot;https://github.com/RamblingCookieMonster/PSStackExchange/blob/db1277453374cb16684b35cf93a8f5c97288c41f/PSStackExchange/PSStackExchange.psm1&quot;&gt;PSStackExchange.psm1&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Exported (Public) Functions&lt;/strong&gt;. These are the advanced functions an end user can run from our module. For example, &lt;a href=&quot;https://github.com/RamblingCookieMonster/PSStackExchange/blob/db1277453374cb16684b35cf93a8f5c97288c41f/PSStackExchange/Public/Get-SEQuestion.ps1&quot;&gt;Get-SEQuestion.ps1&lt;/a&gt; or &lt;a href=&quot;https://github.com/RamblingCookieMonster/PSStackExchange/blob/db1277453374cb16684b35cf93a8f5c97288c41f/PSStackExchange/Public/Get-SEObject.ps1&quot;&gt;Get-SEObject.ps1&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Private Functions&lt;/strong&gt;. These are optional “helper functions” that we want to use in our exported functions, that the end user shouldn’t see. For example, &lt;a href=&quot;https://github.com/RamblingCookieMonster/PSStackExchange/blob/db1277453374cb16684b35cf93a8f5c97288c41f/PSStackExchange/Private/Add-ObjectDetail.ps1&quot;&gt;Add-ObjectDetail.ps1&lt;/a&gt; or &lt;a href=&quot;https://github.com/RamblingCookieMonster/PSStackExchange/blob/db1277453374cb16684b35cf93a8f5c97288c41f/PSStackExchange/Private/Join-Parts.ps1&quot;&gt;Join-Parts.ps1&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Formats&lt;/strong&gt;. These are optional format.ps1xml formats to help &lt;a href=&quot;http://ramblingcookiemonster.github.io/Decorating-Objects/&quot;&gt;decorate your output&lt;/a&gt;, often specified in the module manifest ‘FormatsToProcess’. &lt;a href=&quot;https://github.com/RamblingCookieMonster/PSStackExchange/blob/db1277453374cb16684b35cf93a8f5c97288c41f/PSStackExchange/PSStackExchange.Format.ps1xml&quot;&gt;PSStackExchange.Format.ps1xml&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Readme&lt;/strong&gt;. If you’re &lt;a href=&quot;http://ramblingcookiemonster.github.io/GitHub-For-PowerShell-Projects/&quot;&gt;using GitHub&lt;/a&gt; or another common code repository, the Readme.md is a handy front page for your project, written using simple &lt;a href=&quot;https://help.github.com/articles/github-flavored-markdown/&quot;&gt;Markdown&lt;/a&gt; rather than HTML&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;AppVeyor config&lt;/strong&gt;. If you’re using a supported version control solution, &lt;a href=&quot;http://ramblingcookiemonster.github.io/GitHub-For-PowerShell-Projects/#continuous-integration&quot;&gt;AppVeyor&lt;/a&gt; enables simple and free continuous integration and delivery for open source projects. &lt;a href=&quot;https://github.com/RamblingCookieMonster/PSStackExchange/blob/db1277453374cb16684b35cf93a8f5c97288c41f/appveyor.yml&quot;&gt;AppVeyor.yml&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We have our ingredients, let’s look at a recipe for a module!&lt;/p&gt;

&lt;h3 id=&quot;the-recipe&quot;&gt;The Recipe&lt;/h3&gt;

&lt;p&gt;We’re going to do this in a few quick steps:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Create a GitHub repository&lt;/li&gt;
  &lt;li&gt;Create the module and scaffolding around it&lt;/li&gt;
  &lt;li&gt;Hook up AppVeyor and publish the module&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This might take a few minutes the first time you run through it, but you can borrow and tweak this same scaffolding for each module you write. In fact, you might find or write helper PowerShell modules and tools that simplify this process.&lt;/p&gt;

&lt;p&gt;Let’s get to work!&lt;/p&gt;

&lt;h3 id=&quot;following-the-recipe&quot;&gt;Following the Recipe&lt;/h3&gt;

&lt;p&gt;There’s no real order to this; depending on what you do or don’t incorporate, don’t feel like you need to follow this to the letter.&lt;/p&gt;

&lt;h4 id=&quot;create-a-github-repository&quot;&gt;Create a GitHub Repository.&lt;/h4&gt;

&lt;p&gt;This should be pretty straightforward. If you haven’t used GitHub before, the following might help:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://ramblingcookiemonster.github.io/GitHub-For-PowerShell-Projects&quot;&gt;GitHub for PowerShell Projects&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=wmPfDbsPeZY&quot;&gt;PowerShell.org TechSession: A Crash Course in Version Control and Git&lt;/a&gt; and &lt;a href=&quot;https://github.com/RamblingCookieMonster/Git-Presentation&quot;&gt;presentation materials&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All we need to do is:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Create an account on GitHub, download GitHub for Windows&lt;/li&gt;
  &lt;li&gt;Create a new repository (We’ll call it PSStackExchange, and pick the MIT license)&lt;/li&gt;
  &lt;li&gt;Clone PSStackExchange using GitHub for Windows&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s move on to the most important bit, the module itself.&lt;/p&gt;

&lt;h4 id=&quot;create-the-module-and-scaffolding-around-it&quot;&gt;Create the Module and Scaffolding Around It&lt;/h4&gt;

&lt;p&gt;Here’s how I typically organize my modules. We’ll use PSStackExchange as an example, substitute this out for your own module!&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/RamblingCookieMonster/PSStackExchange/tree/db1277453374cb16684b35cf93a8f5c97288c41f&quot;&gt;PSStackExchange\&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;en-US\ (or locales of choice)
        &lt;ul&gt;
          &lt;li&gt;about_PSStackExchange.help.txt&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;Private&lt;br /&gt;
        &lt;ul&gt;
          &lt;li&gt;Join-Parts.ps1&lt;/li&gt;
          &lt;li&gt;Get-SEData.ps1&lt;/li&gt;
          &lt;li&gt;…&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;Public&lt;br /&gt;
        &lt;ul&gt;
          &lt;li&gt;Get-SEObject.ps1&lt;/li&gt;
          &lt;li&gt;Search-SEQuestion.ps1&lt;/li&gt;
          &lt;li&gt;…&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;lib\ (Not used in this module)
        &lt;ul&gt;
          &lt;li&gt;Some.Library.dll&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;bin\ (Not used in this module)
        &lt;ul&gt;
          &lt;li&gt;SomeDependency.exe&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;PSStackExchange.Format.ps1xml&lt;/li&gt;
      &lt;li&gt;PSStackExchange.psd1&lt;/li&gt;
      &lt;li&gt;PSStackExchange.psm1&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If we’re going to be adding our project to GitHub or a similar code repository, we add a little more scaffolding:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/RamblingCookieMonster/PSStackExchange/tree/db1277453374cb16684b35cf93a8f5c97288c41f&quot;&gt;Repository Root&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;PSStackExchange\ (Module folder described above)&lt;/li&gt;
      &lt;li&gt;Tests&lt;br /&gt;
        &lt;ul&gt;
          &lt;li&gt;PSStackExchange.Tests.ps1&lt;/li&gt;
          &lt;li&gt;Appveyor.Pester.ps1&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;README.md&lt;/li&gt;
      &lt;li&gt;AppVeyor.yml&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I ran through the following code to get started. Typically I’ll just copy the scaffolding from another module, create a new GUID in the psd1, and tweak other module specific references.&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/12c4eb61dde8b2703184.js&quot;&gt; &lt;/script&gt;

&lt;p&gt;In our case, we have a few Stack Exchange advanced functions that hopefully follow &lt;a href=&quot;http://ramblingcookiemonster.github.io/Building-PowerShell-Functions-Best-Practices/&quot;&gt;a few best practices&lt;/a&gt;, some private helper functions that we don’t want the user to see, and a few other files to cover testing and usability.&lt;/p&gt;

&lt;p&gt;In &lt;a href=&quot;https://github.com/RamblingCookieMonster/PSStackExchange/blob/db1277453374cb16684b35cf93a8f5c97288c41f/PSStackExchange/PSStackExchange.psm1&quot;&gt;PSStackExchange.psm1&lt;/a&gt; we load our public and private functions. If a module is a work-in-progress, I’ll usually export $Public.Basename to avoid hard coding functions to export in the psd1. Once a module is released, I try to add the public functions to the psd1.&lt;/p&gt;

&lt;p&gt;If you’re writing a module, you should consider writing &lt;a href=&quot;http://ramblingcookiemonster.github.io/GitHub-Pester-AppVeyor/#pester&quot;&gt;Pester&lt;/a&gt; tests for it. It’s quite comforting to have a suite of tests that run automatically after each change you push, rather than assuming the code you write was correct, or attempting to manually test your code after each change. Give it a shot! We include a few superficial tests in &lt;a href=&quot;https://github.com/RamblingCookieMonster/PSStackExchange/blob/db1277453374cb16684b35cf93a8f5c97288c41f/Tests/PSStackExchange.Tests.ps1&quot;&gt;PSStackExchange.Tests.ps1&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Lastly, we include some usability features. We add an &lt;a href=&quot;https://github.com/RamblingCookieMonster/PSStackExchange/blob/db1277453374cb16684b35cf93a8f5c97288c41f/PSStackExchange/en-US/about_PSStackExchange.help.txt&quot;&gt;about_PSStackExchange&lt;/a&gt; help topic, we &lt;a href=&quot;http://ramblingcookiemonster.github.io/Decorating-Objects/&quot;&gt;decorate our output&lt;/a&gt; with the &lt;a href=&quot;https://github.com/RamblingCookieMonster/PSStackExchange/blob/db1277453374cb16684b35cf93a8f5c97288c41f/PSStackExchange/PSStackExchange.Format.ps1xml&quot;&gt;PSStackExchange.Format.ps1xml file&lt;/a&gt;, and we add some notes on how to install and use the module in the &lt;a href=&quot;https://github.com/RamblingCookieMonster/PSStackExchange/blob/db1277453374cb16684b35cf93a8f5c97288c41f/README.md&quot;&gt;README.md&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We’re good to go! Let’s look at how we can publish this module for others to use and improve.&lt;/p&gt;

&lt;h4 id=&quot;hook-up-appveyor-and-publish-the-module&quot;&gt;Hook up AppVeyor and Publish the Module&lt;/h4&gt;

&lt;p&gt;The content of our module is ready to publish. Before we publish this, we’ll enable continuous integration with some handy automated testing through AppVeyor.&lt;/p&gt;

&lt;p&gt;First, we &lt;a href=&quot;http://ramblingcookiemonster.github.io/GitHub-Pester-AppVeyor/&quot;&gt;set up our project in AppVeyor&lt;/a&gt; by adding &lt;a href=&quot;https://github.com/RamblingCookieMonster/PSStackExchange/blob/master/appveyor.yml&quot;&gt;appveyor.yml&lt;/a&gt; to the repository, and adding the GitHub project to our AppVeyor account. We abstract out the calls to Pester in &lt;a href=&quot;https://github.com/RamblingCookieMonster/PSStackExchange/blob/db1277453374cb16684b35cf93a8f5c97288c41f/Tests/appveyor.pester.ps1&quot;&gt;AppVeyor.Pester.ps1&lt;/a&gt;, using some &lt;a href=&quot;http://ramblingcookiemonster.github.io/Github-Pester-AppVeyor-Part-2/&quot;&gt;ideas from here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Next, we push the changes we’ve made on our computer up to GitHub. Our code is now published, and AppVeyor will start &lt;a href=&quot;https://ci.appveyor.com/project/RamblingCookieMonster/psstackexchange/build/1.0.3&quot;&gt;running a build&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Lastly, we want to publish our module in the &lt;a href=&quot;https://www.powershellgallery.com/&quot;&gt;PowerShell Gallery&lt;/a&gt;, giving end users with PowerShell 5 a simple way to find and install your module. We could hook this up to automatically run in AppVeyor, but that’s &lt;a href=&quot;http://ramblingcookiemonster.github.io/PSDeploy-Inception/&quot;&gt;a topic for later&lt;/a&gt;.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Sign on to PowerShellGallery.com with your Microsoft account&lt;/li&gt;
  &lt;li&gt;Get your API key (find it &lt;a href=&quot;https://www.powershellgallery.com/account&quot;&gt;here&lt;/a&gt;)&lt;/li&gt;
  &lt;li&gt;Publish your module!&lt;/li&gt;
&lt;/ul&gt;

&lt;script src=&quot;https://gist.github.com/e7b381e6bf42c411765e.js&quot;&gt; &lt;/script&gt;

&lt;p&gt;Our module &lt;a href=&quot;https://www.powershellgallery.com/packages/PSStackExchange/&quot;&gt;is now live&lt;/a&gt; on PowerShell Gallery!&lt;/p&gt;

&lt;h3 id=&quot;how-i-write-modules-summarized&quot;&gt;How I Write Modules, Summarized&lt;/h3&gt;

&lt;p&gt;Whew! That was a long post. Thankfully, most of this stuff can be re-used in each module you write. Let’s review the steps:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Create a GitHub repository&lt;/li&gt;
  &lt;li&gt;Create the module and scaffolding around it&lt;/li&gt;
  &lt;li&gt;Hook up AppVeyor and publish the module&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The first and last step take a minute or two each. The module and scaffolding around it can be copied and tweaked, which should only take a few minutes. Most of your time will be spent writing the advanced functions for the module.&lt;/p&gt;

&lt;h3 id=&quot;psstackexchange&quot;&gt;PSStackExchange&lt;/h3&gt;

&lt;p&gt;The module is published and ready to use! I’m on another computer with PowerShell 5, I can get up and running with a few lines of code:&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/09f717821ce056a6dfe2.js&quot;&gt; &lt;/script&gt;

&lt;p&gt;Here’s some output from the examples:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/module/Get-SEObject.png&quot; alt=&quot;Get-SEObject&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/module/Search-SEQuestion.png&quot; alt=&quot;Search-SEQuestion&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;wrapping-up&quot;&gt;Wrapping Up&lt;/h3&gt;

&lt;p&gt;That’s about it! If you aren’t writing modules already, you should definitely consider it. Looking for further reading? Here are a few references that might come in handy:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/RamblingCookieMonster/RamblingCookieMonster.github.io/blob/master/images/module/PSSummit2014-Freiheit-ModuleDesignRules.pptx?raw=true&quot;&gt;Module Design Rules&lt;/a&gt; - This is from the 2014 PowerShell Summit, thanks to Kirk Freiheit&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.simple-talk.com/dotnet/.net-tools/further-down-the-rabbit-hole-powershell-modules-and-encapsulation/&quot;&gt;Further Down the Rabbit Hole: PowerShell Modules and Encapsulation&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://msdn.microsoft.com/en-us/library/dd878297&quot;&gt;How to Write a Module Manifest&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://msdn.microsoft.com/en-us/library/dd878324(v=vs.85).aspx&quot;&gt;Windows PowerShell Modules&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://technet.microsoft.com/en-us/library/hh847804.aspx&quot;&gt;about_Modules&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://ramblingcookiemonster.wordpress.com/2013/12/08/building-powershell-functions-best-practices/&quot;&gt;Building PowerShell Functions - Best Practices&lt;/a&gt; - Shameless plug. Includes a number of references.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.manning.com/books/learn-powershell-toolmaking-in-a-month-of-lunches&quot;&gt;Learn PowerShell Toolmaking in a Month of Lunches&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://mikefrobbins.com/2015/04/17/free-ebook-on-powershell-advanced-functions/&quot;&gt;Free eBook on PowerShell Advanced Functions&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/PoshCode/PowerShellPracticeAndStyle&quot;&gt;The PowerShell Best Practices and Style Guide&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.jsnover.com/Docs/MonadManifesto.pdf&quot;&gt;Monad Manifesto&lt;/a&gt; - This gives a nice overview of the vision and goals set out for PowerShell. If you’re writing modules for public consumption, consider reading this, to avoid publishing something as awful as Citrix’ PVS “PowerShell” snapin.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;side-note-for-vendors&quot;&gt;Side Note for Vendors&lt;/h4&gt;

&lt;p&gt;Writing PSStackExchange &lt;a href=&quot;http://ramblingcookiemonster.github.io/REST-PowerShell-and-Infoblox/&quot;&gt;reminded me&lt;/a&gt; how important it is for vendors of enterprise products to provide PowerShell modules that wrap their product’s API. Despite a nice API and decent documentation, writing a feature-poor PowerShell module for this was just as painful as &lt;a href=&quot;http://ramblingcookiemonster.github.io/Querying-the-Infoblox-Web-API/&quot;&gt;wrapping the Infoblox API&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Vendors: If your competition provides a PowerShell module and you do not, there’s a good chance I’ll push for your competitor’s product. This is a major value-add if you do it right and follow PowerShell conventions.&lt;/p&gt;

&lt;p&gt;Cheers!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;EDIT July 2016&lt;/em&gt;: Updated links to PSStackExchange to &lt;a href=&quot;https://twitter.com/psCookieMonster/status/757372331362779136&quot;&gt;link to a specific point in time&lt;/a&gt;.  The current version of this project may see updates to illustrate things like PSDeploy.&lt;/p&gt;

    &lt;p&gt;&lt;a href=&quot;http://ramblingcookiemonster.github.io/Building-A-PowerShell-Module/&quot;&gt;Building a PowerShell Module&lt;/a&gt; was originally published by Warren F at &lt;a href=&quot;http://ramblingcookiemonster.github.io&quot;&gt;Rambling Cookie Monster&lt;/a&gt; on September 06, 2015.&lt;/p&gt;
  </content>
</entry>


<entry>
  <title type="html"><![CDATA[PowerShell Configuration Data]]></title>
  <link rel="alternate" type="text/html" href="http://ramblingcookiemonster.github.io/PowerShell-Configuration-Data/" />
  <id>http://ramblingcookiemonster.github.io/PowerShell-Configuration-Data</id>
  <updated>2015-08-16T22:00:00+00:00</updated>
  <published>2015-08-16T22:00:00+00:00</published>
  
  <author>
    <name>Warren F</name>
    <uri>http://ramblingcookiemonster.github.io</uri>
    
  </author>
  <content type="html">
    &lt;section id=&quot;table-of-contents&quot; class=&quot;toc&quot;&gt;
  &lt;header&gt;
    &lt;h3&gt;&lt;i class=&quot;fa fa-book&quot;&gt;&lt;/i&gt; Overview&lt;/h3&gt;
  &lt;/header&gt;
&lt;div id=&quot;drawer&quot;&gt;
&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#rambling&quot; id=&quot;markdown-toc-rambling&quot;&gt;Rambling&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#the-choices&quot; id=&quot;markdown-toc-the-choices&quot;&gt;The Choices&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#xml&quot; id=&quot;markdown-toc-xml&quot;&gt;XML&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#common-tools&quot; id=&quot;markdown-toc-common-tools&quot;&gt;Common tools&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#the-good&quot; id=&quot;markdown-toc-the-good&quot;&gt;The Good&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#the-bad&quot; id=&quot;markdown-toc-the-bad&quot;&gt;The Bad&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#example&quot; id=&quot;markdown-toc-example&quot;&gt;Example&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#should-i-use-xml&quot; id=&quot;markdown-toc-should-i-use-xml&quot;&gt;Should I use XML?&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#further-reading&quot; id=&quot;markdown-toc-further-reading&quot;&gt;Further reading&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#json&quot; id=&quot;markdown-toc-json&quot;&gt;JSON&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#common-tools-1&quot; id=&quot;markdown-toc-common-tools-1&quot;&gt;Common tools&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#the-good-1&quot; id=&quot;markdown-toc-the-good-1&quot;&gt;The Good&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#the-bad-1&quot; id=&quot;markdown-toc-the-bad-1&quot;&gt;The Bad&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#example-1&quot; id=&quot;markdown-toc-example-1&quot;&gt;Example&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#should-i-use-json&quot; id=&quot;markdown-toc-should-i-use-json&quot;&gt;Should I use JSON?&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#further-reading-1&quot; id=&quot;markdown-toc-further-reading-1&quot;&gt;Further reading&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#yaml&quot; id=&quot;markdown-toc-yaml&quot;&gt;YAML&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#common-tools-2&quot; id=&quot;markdown-toc-common-tools-2&quot;&gt;Common tools&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#the-good-2&quot; id=&quot;markdown-toc-the-good-2&quot;&gt;The Good&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#the-bad-2&quot; id=&quot;markdown-toc-the-bad-2&quot;&gt;The Bad&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#example-2&quot; id=&quot;markdown-toc-example-2&quot;&gt;Example&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#should-i-use-yaml&quot; id=&quot;markdown-toc-should-i-use-yaml&quot;&gt;Should I use YAML?&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#powershell-data-file-psd1&quot; id=&quot;markdown-toc-powershell-data-file-psd1&quot;&gt;PowerShell Data File (PSD1)&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#common-tools-3&quot; id=&quot;markdown-toc-common-tools-3&quot;&gt;Common tools&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#the-good-3&quot; id=&quot;markdown-toc-the-good-3&quot;&gt;The Good&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#the-bad-3&quot; id=&quot;markdown-toc-the-bad-3&quot;&gt;The Bad&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#example-3&quot; id=&quot;markdown-toc-example-3&quot;&gt;Example&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#should-i-use-psd1&quot; id=&quot;markdown-toc-should-i-use-psd1&quot;&gt;Should I use PSD1?&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#other-data-formats&quot; id=&quot;markdown-toc-other-data-formats&quot;&gt;Other Data Formats&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#what-should-i-use&quot; id=&quot;markdown-toc-what-should-i-use&quot;&gt;What Should I Use?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

  &lt;/div&gt;
&lt;/section&gt;
&lt;!-- /#table-of-contents --&gt;

&lt;h3 id=&quot;rambling&quot;&gt;Rambling&lt;/h3&gt;

&lt;p&gt;I recently wrote &lt;a href=&quot;https://ramblingcookiemonster.github.io/PSDeploy/&quot;&gt;PSDeploy&lt;/a&gt;, a quick-and-dirty module to abstract out PowerShell based deployments. You define what you want deployed in a configuration file, it does the rest.&lt;/p&gt;

&lt;p&gt;The most common follow-up questions seemed to be &lt;em&gt;why yaml?&lt;/em&gt; or &lt;em&gt;why not &amp;#60;data format of preference&amp;#62;?&lt;/em&gt; This is a quick hit on the many data formats you can use from PowerShell.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;Disclaimer&lt;/strong&gt;: I know very little about data formats, their intended uses, or their benefits or caveats. This is from a layman’s perspective.&lt;/em&gt;&lt;/p&gt;

&lt;h3 id=&quot;the-choices&quot;&gt;The Choices&lt;/h3&gt;

&lt;p&gt;Let’s list off a few common data formats we could use.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;XML&lt;/li&gt;
  &lt;li&gt;JSON&lt;/li&gt;
  &lt;li&gt;YAML&lt;/li&gt;
  &lt;li&gt;PowerShell Data File (PSD1)&lt;/li&gt;
  &lt;li&gt;INI&lt;/li&gt;
  &lt;li&gt;Registry&lt;/li&gt;
  &lt;li&gt;CSV&lt;/li&gt;
  &lt;li&gt;Text&lt;/li&gt;
  &lt;li&gt;Database&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s take a peak at these from a high level. Keep in mind these can vary wildly even within a single format. For example, you might have an ad hoc JSON file, or a formal schema describing your JSON.&lt;/p&gt;

&lt;p&gt;For each solution you design, consider your needs, priorities, and constraints, and pair them up with one of these formats. Bear in mind the the lessons of the &lt;a href=&quot;http://mikehadlow.blogspot.com/2012/05/configuration-complexity-clock.html&quot;&gt;configuration complexity clock&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;xml&quot;&gt;XML&lt;/h3&gt;

&lt;p&gt;XML is a tried and true format, but is a bit inefficient and ugly.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/formats/xml.png&quot; alt=&quot;XML example&quot; /&gt;&lt;/p&gt;

&lt;h4 id=&quot;common-tools&quot;&gt;Common tools&lt;/h4&gt;

&lt;ul&gt;
  &lt;li&gt;Built in Cmdlets: Import-CliXml, Export-CliXml, ConvertTo-Xml&lt;/li&gt;
  &lt;li&gt;Built in .NET framework support&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;the-good&quot;&gt;The Good&lt;/h4&gt;

&lt;ul&gt;
  &lt;li&gt;Avoid external dependencies&lt;/li&gt;
  &lt;li&gt;Flexible&lt;/li&gt;
  &lt;li&gt;Wide cross-platform support and tooling&lt;/li&gt;
  &lt;li&gt;CliXml functions provide simple serialization and deserialization&lt;/li&gt;
  &lt;li&gt;Stores data with more than one layer of depth&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;the-bad&quot;&gt;The Bad&lt;/h4&gt;

&lt;ul&gt;
  &lt;li&gt;Not human readable&lt;/li&gt;
  &lt;li&gt;Syntax is inefficient and verbose&lt;/li&gt;
  &lt;li&gt;It’s 2015&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;example&quot;&gt;Example&lt;/h4&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Serialize some data to disk&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;Get-Item &lt;/span&gt;C:\Windows\explorer.exe |
    &lt;span class=&quot;nb&quot;&gt;Export-Clixml&lt;/span&gt; -Depth 5 -Path C:\XML.xml

&lt;span class=&quot;c1&quot;&gt;# Deserialize the data&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$File&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Import-Clixml&lt;/span&gt; -Path C:\XML.xml

&lt;span class=&quot;c1&quot;&gt;# Drill down.&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$File&lt;/span&gt;.VersionInfo.ProductVersion
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;should-i-use-xml&quot;&gt;Should I use XML?&lt;/h4&gt;

&lt;p&gt;There are two scenarios where I use XML. In all other cases I pick an alternative.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Quick and dirty serialization. Import and Export CliXml are simple to use&lt;/li&gt;
  &lt;li&gt;Technology lock-in. If it only supports XML, you don’t have a choice&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;further-reading&quot;&gt;Further reading&lt;/h4&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.powershellmagazine.com/2013/08/19/mastering-everyday-xml-tasks-in-powershell/&quot;&gt;Mastering everyday XML tasks in PowerShell&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.codeproject.com/Articles/61900/PowerShell-and-XML&quot;&gt;PowerShell and XML&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;json&quot;&gt;JSON&lt;/h3&gt;

&lt;p&gt;JSON is a lightweight data format common in modern web APIs.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/formats/json.png&quot; alt=&quot;JSON example&quot; /&gt;&lt;/p&gt;

&lt;h4 id=&quot;common-tools-1&quot;&gt;Common tools&lt;/h4&gt;

&lt;ul&gt;
  &lt;li&gt;Built in Cmdlets: ConvertFrom-Json, ConvertTo-Json (PowerShell 3 or later)&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;the-good-1&quot;&gt;The Good&lt;/h4&gt;

&lt;ul&gt;
  &lt;li&gt;Avoid external dependencies (in PowerShell 3 or later)&lt;/li&gt;
  &lt;li&gt;Stores data with more than one layer of depth&lt;/li&gt;
  &lt;li&gt;Semi human readable&lt;/li&gt;
  &lt;li&gt;Syntax is more efficient and less verbose than XML&lt;/li&gt;
  &lt;li&gt;Implemented in libraries across several languages&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;the-bad-1&quot;&gt;The Bad&lt;/h4&gt;

&lt;ul&gt;
  &lt;li&gt;Want to store MSFT paths? Have fun: &lt;code class=&quot;highlighter-rouge&quot;&gt;{ &quot;Path&quot;:  &quot;C:\\W\\T\\F&quot; }&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Not as widespread cross-platform support or tooling as XML&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;example-1&quot;&gt;Example&lt;/h4&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Convert some data to Json&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$JSON&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Get-Item &lt;/span&gt;C:\Windows\explorer.exe |
    &lt;span class=&quot;nb&quot;&gt;ConvertTo-Json&lt;/span&gt; -Depth 2

&lt;span class=&quot;c1&quot;&gt;# Read the Json back into an object&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$File&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$JSON&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;ConvertFrom-Json&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Drill down.&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$File&lt;/span&gt;.VersionInfo.ProductVersion
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;should-i-use-json&quot;&gt;Should I use JSON?&lt;/h4&gt;

&lt;p&gt;JSON is a popular and safe choice nowadays. Depending on your needs, this is often a good fit.&lt;/p&gt;

&lt;h4 id=&quot;further-reading-1&quot;&gt;Further reading&lt;/h4&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.powershellmagazine.com/2014/12/01/a-json-primer-for-administrators/&quot;&gt;A JSON Primer for Administrators&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/JamesNK/Newtonsoft.Json&quot;&gt;JSON.NET&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://blogs.technet.com/b/heyscriptingguy/archive/2014/04/23/json-is-the-new-xml.aspx&quot;&gt;JSON Is the New XML&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;yaml&quot;&gt;YAML&lt;/h3&gt;

&lt;p&gt;YAML is a human friendly data format.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/formats/yaml.png&quot; alt=&quot;yaml example&quot; /&gt;&lt;/p&gt;

&lt;h4 id=&quot;common-tools-2&quot;&gt;Common tools&lt;/h4&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/scottmuc/PowerYaml&quot;&gt;PowerYaml&lt;/a&gt; and various &lt;a href=&quot;https://github.com/cdhunt/PowerYaml&quot;&gt;forks&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/aaubry/YamlDotNet&quot;&gt;Yaml.Net&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;the-good-2&quot;&gt;The Good&lt;/h4&gt;

&lt;ul&gt;
  &lt;li&gt;Very human readable&lt;/li&gt;
  &lt;li&gt;Stores data with more than one layer of depth&lt;/li&gt;
  &lt;li&gt;Syntax is more efficient and less verbose than XML&lt;/li&gt;
  &lt;li&gt;Implemented in libraries across several languages&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;the-bad-2&quot;&gt;The Bad&lt;/h4&gt;

&lt;ul&gt;
  &lt;li&gt;Dependencies on libraries like Yaml.Net&lt;/li&gt;
  &lt;li&gt;Haven’t seen a reliable serialize and deserialize module for YAML yet&lt;/li&gt;
  &lt;li&gt;Whitespace is part of the syntax&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;example-2&quot;&gt;Example&lt;/h4&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Define some Yaml&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$Yaml&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; @&lt;span class=&quot;s2&quot;&gt;&quot;
Name: explorer.exe
Length: 4532304
DirectoryName: C:\Windows
VersionInfo:
  ProductVersion: 10.0.10240.16384
&quot;&lt;/span&gt;@

&lt;span class=&quot;c1&quot;&gt;#Read the Yaml, using PowerYaml&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$File&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; Get-Yaml -FromString &lt;span class=&quot;nv&quot;&gt;$Yaml&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Drill down.&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$File&lt;/span&gt;.VersionInfo.ProductVersion
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;should-i-use-yaml&quot;&gt;Should I use YAML?&lt;/h4&gt;

&lt;p&gt;I would only recommend YAML where human readability is paramount, and your other needs and constraints don’t rule it out.&lt;/p&gt;

&lt;p&gt;Yaml a great option when you will simply be reading in a config file, and the end users will be manually manipulating this file.&lt;/p&gt;

&lt;h3 id=&quot;powershell-data-file-psd1&quot;&gt;PowerShell Data File (PSD1)&lt;/h3&gt;

&lt;p&gt;PowerShell data files are used for PowerShell module manifests, but can be used to store arbitrary data.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/formats/psd1.png&quot; alt=&quot;psd1 example&quot; /&gt;&lt;/p&gt;

&lt;h4 id=&quot;common-tools-3&quot;&gt;Common tools&lt;/h4&gt;

&lt;ul&gt;
  &lt;li&gt;Built in Cmdlet: &lt;a href=&quot;https://technet.microsoft.com/en-us/library/hh849919.aspx&quot;&gt;Import-LocalizedData&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;the-good-3&quot;&gt;The Good&lt;/h4&gt;

&lt;ul&gt;
  &lt;li&gt;Familiar to PowerShell authors&lt;/li&gt;
  &lt;li&gt;Avoid external dependencies&lt;/li&gt;
  &lt;li&gt;Stores data with more than one layer of depth&lt;/li&gt;
  &lt;li&gt;Semi human readable&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;the-bad-3&quot;&gt;The Bad&lt;/h4&gt;

&lt;ul&gt;
  &lt;li&gt;Single platform&lt;/li&gt;
  &lt;li&gt;Haven’t seen a reliable serialize and deserialize module for PSD1 files yet&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;example-3&quot;&gt;Example&lt;/h4&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Create a PSD1 file&lt;/span&gt;
@&lt;span class=&quot;s2&quot;&gt;&quot;
@{
    Name = 'explorer.exe'
    Length = 4532304
    DirectoryName = 'C:\Windows'
    VersionInfo = @{
      ProductVersion = '10.0.10240.16384'
    }
}
&quot;&lt;/span&gt;@ | &lt;span class=&quot;nb&quot;&gt;Out-File&lt;/span&gt; -FilePath C:\PSD1.psd1

&lt;span class=&quot;c1&quot;&gt;# Read the file&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$File&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Import-LocalizedData&lt;/span&gt; -BaseDirectory C:\ -FileName PSD1.psd1

&lt;span class=&quot;c1&quot;&gt;# Drill down.&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$File&lt;/span&gt;.VersionInfo.ProductVersion
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;should-i-use-psd1&quot;&gt;Should I use PSD1?&lt;/h4&gt;

&lt;p&gt;This seems like a reasonable choice for PowerShell configuration files that will be edited by hand, by folks familiar with PowerShell.&lt;/p&gt;

&lt;h3 id=&quot;other-data-formats&quot;&gt;Other Data Formats&lt;/h3&gt;

&lt;p&gt;There are a variety of other choices. Here are a few others you might consider:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;CSV&lt;/strong&gt; has plenty of built in Cmdlets, but is quite limited and might produce &lt;a href=&quot;http://learn-powershell.net/2014/01/24/avoiding-system-object-or-similar-output-when-using-export-csv/&quot;&gt;unexpected results&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Text&lt;/strong&gt; is an option, with a variety of tools including the new &lt;a href=&quot;http://www.powershellmagazine.com/2014/09/09/using-the-convertfrom-string-cmdlet-to-parse-structured-text/&quot;&gt;ConvertFrom-String&lt;/a&gt;. Not sure why you would chose this over an existing data format.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;The registry&lt;/strong&gt;. I wouldn’t pick it, but it’s used by many applications and is familiar to most Windows administrators. Tools like &lt;a href=&quot;https://psremoteregistry.codeplex.com/releases/view/65928&quot;&gt;PSRemoteRegistry&lt;/a&gt; make this simple to work with remotely, unlike the registry PSProvider.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;a href=&quot;http://lipkau.github.io/PsIni/&quot;&gt;Ini files&lt;/a&gt;&lt;/strong&gt; are a bit dated, but are simple to read and use. Given their limitations and the wealth of other options, you should probably leave these in the attic.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Databases&lt;/strong&gt; are a great option for larger solutions, or where your data model requires a bit more sophistication. &lt;a href=&quot;http://ramblingcookiemonster.github.io/SQLite-and-PowerShell/&quot;&gt;SQLite&lt;/a&gt; is a handy, cross-platform solution, if SQL Server or other database engines are too heavy-weight.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;what-should-i-use&quot;&gt;What Should I Use?&lt;/h3&gt;

&lt;p&gt;Like most answers in the world of computing: It depends. Chances are, even with your particular needs and constraints, there isn’t one correct choice. Consider your options, and pick a data format that makes sense to you.&lt;/p&gt;

&lt;p&gt;Do you enjoy trolling? Consider JSONx:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://twitter.com/DanHarper7/status/514822464673951744&quot;&gt;&lt;img src=&quot;/images/formats/jsonx.png&quot; alt=&quot;JSONx tweet&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Cheers!&lt;/p&gt;

    &lt;p&gt;&lt;a href=&quot;http://ramblingcookiemonster.github.io/PowerShell-Configuration-Data/&quot;&gt;PowerShell Configuration Data&lt;/a&gt; was originally published by Warren F at &lt;a href=&quot;http://ramblingcookiemonster.github.io&quot;&gt;Rambling Cookie Monster&lt;/a&gt; on August 16, 2015.&lt;/p&gt;
  </content>
</entry>


<entry>
  <title type="html"><![CDATA[PSDeploy&#58; Simplified PowerShell Based Deployments]]></title>
  <link rel="alternate" type="text/html" href="http://ramblingcookiemonster.github.io/PSDeploy/" />
  <id>http://ramblingcookiemonster.github.io/PSDeploy</id>
  <updated>2015-08-15T22:00:00+00:00</updated>
  <published>2015-08-15T22:00:00+00:00</published>
  
  <author>
    <name>Warren F</name>
    <uri>http://ramblingcookiemonster.github.io</uri>
    
  </author>
  <content type="html">
    &lt;section id=&quot;table-of-contents&quot; class=&quot;toc&quot;&gt;
  &lt;header&gt;
    &lt;h3&gt;&lt;i class=&quot;fa fa-book&quot;&gt;&lt;/i&gt; Overview&lt;/h3&gt;
  &lt;/header&gt;
&lt;div id=&quot;drawer&quot;&gt;
&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#rambling&quot; id=&quot;markdown-toc-rambling&quot;&gt;Rambling&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#continuous-deployment&quot; id=&quot;markdown-toc-continuous-deployment&quot;&gt;Continuous Deployment&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#psdeploy&quot; id=&quot;markdown-toc-psdeploy&quot;&gt;PSDeploy&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#ingredients&quot; id=&quot;markdown-toc-ingredients&quot;&gt;Ingredients&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#a-simple-illustration&quot; id=&quot;markdown-toc-a-simple-illustration&quot;&gt;A Simple Illustration&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#getting-started&quot; id=&quot;markdown-toc-getting-started&quot;&gt;Getting Started&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#example-deployment-scenarios&quot; id=&quot;markdown-toc-example-deployment-scenarios&quot;&gt;Example Deployment Scenarios&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#basic-filesystem-deployment&quot; id=&quot;markdown-toc-basic-filesystem-deployment&quot;&gt;Basic Filesystem Deployment&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#filesystemremote-deployment&quot; id=&quot;markdown-toc-filesystemremote-deployment&quot;&gt;FilesystemRemote Deployment&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#extending-psdeploy&quot; id=&quot;markdown-toc-extending-psdeploy&quot;&gt;Extending PSDeploy&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#update-psdeployyml&quot; id=&quot;markdown-toc-update-psdeployyml&quot;&gt;Update PSDeploy.yml&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#create-the-deployment-script&quot; id=&quot;markdown-toc-create-the-deployment-script&quot;&gt;Create the Deployment Script&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#yaml-options&quot; id=&quot;markdown-toc-yaml-options&quot;&gt;YAML Options&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#next-steps&quot; id=&quot;markdown-toc-next-steps&quot;&gt;Next Steps&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

  &lt;/div&gt;
&lt;/section&gt;
&lt;!-- /#table-of-contents --&gt;

&lt;h3 id=&quot;rambling&quot;&gt;Rambling&lt;/h3&gt;

&lt;p&gt;A short while back I gave my first live &lt;a href=&quot;https://github.com/RamblingCookieMonster/Git-Presentation&quot;&gt;webinar&lt;/a&gt;, on getting started with Git and GitHub. It was both exciting and a bit terrifying. I’m gearing up for similar sessions at work to get folks going with our version control solution, Stash.&lt;/p&gt;

&lt;p&gt;When I look at version control as an IT professional, one pain point that we could help with stands out. In addition to changing their workflow and learning version control, folks are now working with files and folders in source control. Not where they live. Wouldn’t it be handy if we could use version control, and not worry about remembering to be sure to move the files to their appropriate homes?&lt;/p&gt;

&lt;p&gt;This post will discuss a simple PowerShell based deployment solution: &lt;a href=&quot;https://github.com/RamblingCookieMonster/PSDeploy&quot;&gt;PSDeploy&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;continuous-deployment&quot;&gt;Continuous Deployment&lt;/h3&gt;

&lt;p&gt;Developers realized this was important long ago. There are some specialized deployment tools like &lt;a href=&quot;http://octopusdeploy.com/&quot;&gt;Octopus Deploy&lt;/a&gt;, continuous integration and deployment tools like &lt;a href=&quot;http://www.appveyor.com/&quot;&gt;AppVeyor&lt;/a&gt; or &lt;a href=&quot;https://www.jetbrains.com/teamcity/&quot;&gt;TeamCity&lt;/a&gt;, and continuous integration tools like &lt;a href=&quot;http://jenkins-ci.org/&quot;&gt;Jenkins&lt;/a&gt; that can be shoehorned into providing deployments.&lt;/p&gt;

&lt;p&gt;So, with all these solutions, why duct-tape something together?&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;I like fun side projects&lt;/li&gt;
  &lt;li&gt;A simplified deployment system might help with buy in for our version control solution&lt;/li&gt;
  &lt;li&gt;We use &lt;a href=&quot;https://www.hodgkins.net.au/powershell/automating-with-jenkins-and-powershell-on-windows-part-1/&quot;&gt;Jenkins&lt;/a&gt;. Their build definition process leaves a bit to be desired. This allows me to use the same abstracted build script for every project, as long as I have a deployments.yml in the repo root&lt;/li&gt;
  &lt;li&gt;This is portable, and might help folks who use more than one tool chain. I could use PSDeploy with Jenkins, TeamCity, AppVeyor, Bamboo, etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s dive in.&lt;/p&gt;

&lt;h3 id=&quot;psdeploy&quot;&gt;PSDeploy&lt;/h3&gt;

&lt;p&gt;This is a quick and dirty module that simplifies deployments. You create a deployment config file, PSDeploy reads it and runs your deployment(s).&lt;/p&gt;

&lt;h4 id=&quot;ingredients&quot;&gt;Ingredients&lt;/h4&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;YAML&lt;/strong&gt;: &lt;a href=&quot;https://en.wikipedia.org/wiki/YAML&quot;&gt;Data format&lt;/a&gt; for deployment config files. Even easier to read than JSON. Familiar to folks using tools like AppVeyor.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Deployment config&lt;/strong&gt;: YAML files defining what is being deployed. They should have a source, a destination, a deployment type, and might contain freeform deployment options.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Deployment type&lt;/strong&gt;: These define how to actually deploy something. Each type is associated with a script. We’re including FileSystem and FileSystemRemote to start, but this is extensible.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Deployment script&lt;/strong&gt;: These are scripts associated with a particular deployment type. All should accept a ‘Deployment’ parameter. For example, the FileSystem script uses robocopy and copy-item to deploy folders and files, respectively.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;a-simple-illustration&quot;&gt;A Simple Illustration&lt;/h4&gt;

&lt;p&gt;Let’s looks at an example deployments.yml and the outcome of invoking it.&lt;/p&gt;

&lt;p&gt;Here’s a project I’m working on. I want to deploy MyModule to a few paths every time I push a commit. I should probably gate this with Pester tests as well : )&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/psdeploy/module.png&quot; alt=&quot;Module folder&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Here are the content of the deployments.yml:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;MyModuleDeployment&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;Author&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;wframe'&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;Source&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;MyModule'&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;Destination&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;C:\Temp\MyModule'&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;\\C-IS-TS-91\C$\Users\wframem\documents\WindowsPowerShell\Modules\MyModule'&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;DeploymentType&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Filesystem&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;Options&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Mirror&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;True&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We can verify how PSDeploy will parse this:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/psdeploy/get-psdeployment.png&quot; alt=&quot;Get-PSDeployment&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Let’s invoke the deployment:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/psdeploy/mymoduledeployment.png&quot; alt=&quot;Invoke-PSDeployment&quot; /&gt;&lt;/p&gt;

&lt;p&gt;If we check the destination paths, we find MyModule has been deployed!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/psdeploy/deployed.png&quot; alt=&quot;Deployed&quot; /&gt;&lt;/p&gt;

&lt;p&gt;What happened under the hood?&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;We read the deployments.yml&lt;/li&gt;
  &lt;li&gt;We see that the source is a folder&lt;/li&gt;
  &lt;li&gt;We see that the deployment type is Filesystem&lt;/li&gt;
  &lt;li&gt;We look up the script for filesystem deployments&lt;/li&gt;
  &lt;li&gt;We execute the filesystem script for this deployment&lt;/li&gt;
  &lt;li&gt;The script reads the deployment and processes it:
    &lt;ul&gt;
      &lt;li&gt;It’s a folder, so we use robocopy /E /XO&lt;/li&gt;
      &lt;li&gt;The ‘mirror’ deployment option is set, so we add on /PURGE&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you prefer illustrations, here’s quick diagram of a similar deployment:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://cloud.githubusercontent.com/assets/6377597/9177949/7c5a0c26-3f62-11e5-9d31-61f74a324383.png&quot;&gt;&lt;img src=&quot;https://cloud.githubusercontent.com/assets/6377597/9177951/7fec1fa0-3f62-11e5-98bc-6a077d1c57f0.png&quot; alt=&quot;psdeployflowsmall&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That’s pretty much it! Instead of defining all this code in my continuous deployment system, I just run Invoke-PSDeployment.&lt;/p&gt;

&lt;h4 id=&quot;getting-started&quot;&gt;Getting Started&lt;/h4&gt;

&lt;p&gt;First off, download and explore the module:&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/07701d9e29d55593962a.js&quot;&gt; &lt;/script&gt;

&lt;p&gt;Running through the code above, we see a few commands you can work with:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Get-PSDeployment&lt;/strong&gt;: Read a deployment config file&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Get-PSDeploymentType&lt;/strong&gt;: Get details on a deployment type&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Get-PSDeploymentScript&lt;/strong&gt;: Show available deployment types and associated scripts&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Invoke-PSDeployment&lt;/strong&gt;: Run a deployment&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s put this to work in a few real world scenarios.&lt;/p&gt;

&lt;h3 id=&quot;example-deployment-scenarios&quot;&gt;Example Deployment Scenarios&lt;/h3&gt;

&lt;h4 id=&quot;basic-filesystem-deployment&quot;&gt;Basic Filesystem Deployment&lt;/h4&gt;

&lt;p&gt;I want to deploy C:\Git\MyModuleRepo\MyModule to \\Server\Modules. How can I use PSDeploy to do this?&lt;/p&gt;

&lt;p&gt;First, we add a config file, C:\Git\MyModuleRepo\deployments.yml. Here’s the content:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;MyModuleDeployment&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;Author&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;wframe'&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;Source&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;MyModule'&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;Destination&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;\\Server\Modules\MyModule'&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;DeploymentType&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Filesystem&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;Options&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Mirror&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;True&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We verify that the deployment will do what we want:&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/641149d25ad3cfa80529.js&quot;&gt; &lt;/script&gt;

&lt;p&gt;Looks good to me! In our continuous deployment solution, we simply run this:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    Invoke-PSDeployment -Path &lt;span class=&quot;s1&quot;&gt;'C:\Git\MyModuleRepo\deployments.yml'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href=&quot;https://gist.github.com/RamblingCookieMonster/bff0d1a4604ba7a7fd23&quot;&gt;This gist&lt;/a&gt; shows how you can test out PSDeploy without setting up any fancy tools.  All you do is create a dummy folder to deploy, targets to deploy it to, and invoke the deployment.&lt;/p&gt;

&lt;p&gt;Let’s look at a more involved deployment.&lt;/p&gt;

&lt;h4 id=&quot;filesystemremote-deployment&quot;&gt;FilesystemRemote Deployment&lt;/h4&gt;

&lt;p&gt;So! I’m using Jenkins for continuous integration and trying to shoehorn in a deployment. I’m running into trouble: Jenkins is running with an account that doesn’t have the right privileges to deploy to the targets.&lt;/p&gt;

&lt;p&gt;We can use the FilesystemRemote deployment type, which will run the robocopy and copy-item commands in a PowerShell remoting session with appropriate credentials. I know, it’s silly, pull requests for a more appropriate solution would be welcome : )&lt;/p&gt;

&lt;p&gt;Ahead of time, I locked down the Jenkins server and set up EnvInject, with a little help from &lt;a href=&quot;https://www.hodgkins.net.au/powershell/automating-with-jenkins-and-powershell-on-windows-part-2/&quot;&gt;Matt Hodgkins’ post&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Next, I set up a project with the following PowerShell build command. I’m omitting the testing steps for simplicity. You should strongly consider gating your deployments using Pester tests.&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/dd110b56466fd918f672.js&quot;&gt; &lt;/script&gt;

&lt;p&gt;This looks a bit complicated, but all we’re doing is loading up PSDeploy, grabbing credentials for our deployment, and kicking off the deployment with some parameters. You can run &lt;code class=&quot;highlighter-rouge&quot;&gt;Get-Help Invoke-PSDeployment -Parameter DeploymentParameters&lt;/code&gt; for more information on passing parameters to a deployment script.&lt;/p&gt;

&lt;p&gt;That’s about it! Maybe SomeSessionConfig is a delegated endpoint, which works around the double hop issue. I could also configure a &lt;a href=&quot;http://www.powershellmagazine.com/2014/03/06/accidental-sabotage-beware-of-credssp/&quot;&gt;CredSSP&lt;/a&gt; endpoint, but that might draw the wrath of the security community. Tip: Do consider the risks of CredSSP, but if you still have admins using RDP, you’re already using CredSSP, and you have bigger problems to worry about : )&lt;/p&gt;

&lt;p&gt;We just walked through a few example deployment types. What if you want more deployment options?&lt;/p&gt;

&lt;h3 id=&quot;extending-psdeploy&quot;&gt;Extending PSDeploy&lt;/h3&gt;

&lt;p&gt;I’m probably getting ahead of myself, but I tried to make PSDeploy somewhat extensible. Follow these steps to add a new deployment type:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Update PSDeploy.yml (Associates scripts to deployment types)&lt;/li&gt;
  &lt;li&gt;Write the deployment script&lt;/li&gt;
  &lt;li&gt;Consider whether and where to store options in the deployment config&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;update-psdeployyml&quot;&gt;Update PSDeploy.yml&lt;/h4&gt;

&lt;p&gt;This file is stored in the root of the module, although you can move it to a central spot. It stores the associations of deployment types to deployment scripts.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;The deployment type is the root node.&lt;/li&gt;
  &lt;li&gt;The script node defines what script to run for these deployment types&lt;/li&gt;
  &lt;li&gt;The description is exposed when running Get-PSDeploymentType&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, I might add support for SCP deployments:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;SCP&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;Script&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;SCP.ps1&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;Description&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Deploys artifacts using SCP. Requires Posh-SSH&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;create-the-deployment-script&quot;&gt;Create the Deployment Script&lt;/h4&gt;

&lt;p&gt;These are stored in the PSDeploy folder under PSDeployScripts, although you can point to other paths in PSDeploy.yml&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Following the SCP example, our deployment script might be C:\Path\To\PSDeploy\PSDeployScripts\SCP.ps1&lt;/li&gt;
  &lt;li&gt;In your deployment script (SCP.ps1), include a ‘Deployment’ parameter
    &lt;ul&gt;
      &lt;li&gt;See &lt;a href=&quot;https://github.com/RamblingCookieMonster/PSDeploy/blob/master/PSDeploy/PSDeployScripts/FilesystemRemote.ps1&quot;&gt;FilesystemRemote.ps1&lt;/a&gt; for an example&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Include comment based help to explain the deployment and any extra parameters you accept. This is a good spot to describe YAML options, if you use them.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here’s how I implement the deployment parameter in the filesystem deployment scripts:&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/f5cdb592f2bae3039960.js&quot;&gt; &lt;/script&gt;

&lt;h4 id=&quot;yaml-options&quot;&gt;YAML Options&lt;/h4&gt;

&lt;p&gt;If you want to include options in a deployment config file, similar to the mirror option we use in Filesystem deployments, just include them in deployments.yml.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Get-PSDeployment converts the deployment config YAML and processes it into a number of ‘deployment’ objects that are passed to your script.&lt;/li&gt;
  &lt;li&gt;No special steps are needed for the deployment config, just add nodes as needed.
    &lt;ul&gt;
      &lt;li&gt;We extract the YAML Options node with Get-PSDeployment, and process it into a DeploymentOptions property on the Deployment object&lt;/li&gt;
      &lt;li&gt;If you need even more flexibility, we store the raw converted YAML in the Raw property returned from Get-PSDeployment&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here’s an example extended deployment config with a hostname and port option:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;SomeExtendedDeployment&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;Author&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;wframe'&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;Source&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;SomeSourceFolder'&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;Destination&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;/Some/Target/Path'&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;DeploymentType&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;SCP&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;Options&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Hostname&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Server1&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Port&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;22&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We can explore this with Get-PSDeployment to see what we would work with in out deployment script:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/psdeploy/deploymentyml.png&quot; alt=&quot;DeploymentOptions&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;next-steps&quot;&gt;Next Steps&lt;/h3&gt;

&lt;p&gt;That’s pretty much it! I’m hoping that we get more IT professionals using version control, continuous integration, continuous delivery, and similar solutions. We might see more (and better!) tools to simplify and streamline using these solutions as an IT professional.&lt;/p&gt;

&lt;p&gt;Don’t be afraid of tools and ideas traditionally associated with developers or ‘DevOps’. Even if you work in an enterprise that has few if any developers, these tools and the ideas behind them can benefit IT as a whole.&lt;/p&gt;

&lt;p&gt;If you haven’t already, you should consider &lt;a href=&quot;http://stevenmurawski.com/powershell/2015/8/moving-in-to-open-source&quot;&gt;joining the open source community&lt;/a&gt;. Suggestions, pull requests, and other contributions to PSDeploy would be more than welcome!&lt;/p&gt;

    &lt;p&gt;&lt;a href=&quot;http://ramblingcookiemonster.github.io/PSDeploy/&quot;&gt;PSDeploy&amp;#58; Simplified PowerShell Based Deployments&lt;/a&gt; was originally published by Warren F at &lt;a href=&quot;http://ramblingcookiemonster.github.io&quot;&gt;Rambling Cookie Monster&lt;/a&gt; on August 15, 2015.&lt;/p&gt;
  </content>
</entry>

</feed>
