<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://lazywinadmin.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://lazywinadmin.github.io/" rel="alternate" type="text/html" /><updated>2024-09-19T18:44:55+00:00</updated><id>https://lazywinadmin.github.io/feed.xml</id><title type="html">LazyWinAdmin</title><subtitle>Cloud, DevOps and Automation</subtitle><author><name>François-Xavier Cat</name></author><entry><title type="html">Terraform - Uploading a local PowerShell module to an Azure Automation account</title><link href="https://lazywinadmin.github.io/2020/08/terraform_azure-automationacc_psmoduleupload.html" rel="alternate" type="text/html" title="Terraform - Uploading a local PowerShell module to an Azure Automation account" /><published>2020-08-02T00:00:00+00:00</published><updated>2020-08-02T00:00:00+00:00</updated><id>https://lazywinadmin.github.io/2020/08/terraform_azure-automationacc_psmoduleupload</id><content type="html" xml:base="https://lazywinadmin.github.io/2020/08/terraform_azure-automationacc_psmoduleupload.html"><![CDATA[<p class="notice--info"><strong>Test environment</strong> Ubuntu 20.04, Terraform v0.12.28, provider.azurerm v2.18.0</p>

<p>Azure Automation runbooks are a convinient way to run code in the cloud or on-premises (using Hybrid workers). You create a runbook, create a webhook and your code can be pretty much triggered by any event or system.</p>

<p>I had a scenario where some of my runbooks were using a custom PowerShell module that was not publicly available.</p>

<p>This short article document my approach.</p>

<p>While developping some terraform configuration I noticed the <code class="language-plaintext highlighter-rouge">azurerm</code> provider did not offer a way to upload a PowerShell module into the Automation account. You can only provide a <code class="language-plaintext highlighter-rouge">Uri</code> of a module location,… meaning they expect you make it available somewhere to be picked up. This applies to other tool offerings (ARM, API, …).</p>

<p>However, if you use the Azure Portal, you have the option to upload your module as a zip file. Looks like Microsoft provide a Storage Account in the back end, generate a link and pass it other to Azure Automation to import the file.</p>

<p>This is what you would see in the portal after submitting your file:</p>

<p><img src="../../images/2020/2020-08-02/azure_automation_account-upload_module_in_portal.png" alt="" /></p>

<h2 id="uploading-a-psmodule-to-a-storage-account-with-terraform">Uploading a PSModule to a Storage Account with Terraform</h2>

<p>I used Terraform to replicate the Azure Portal functionnality in the following scenario:</p>

<ol>
  <li>Create a Storage Account</li>
  <li>Create a Blob container</li>
  <li>Upload the file</li>
  <li>Create a SAS key (valid for 180 seconds in my case)</li>
  <li>Provide the link to Azure Automation Account to import the module.</li>
</ol>

<p>Here is whole workflow, including the dependencies.</p>

<p><img src="../../images/2020/2020-08-02/azure_automation_account-upload_module-flow.png" alt="" /></p>

<h2 id="azurerm_automation_module-terraform-resource">azurerm_automation_module Terraform resource</h2>

<p>As you can see <a href="https://www.terraform.io/docs/providers/azurerm/r/automation_module.html">in the hashicorp documentation</a>, the terraform resource <code class="language-plaintext highlighter-rouge">azurerm_automation_module</code> only provide a <code class="language-plaintext highlighter-rouge">uri</code> parameter for module to import.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>resource "azurerm_automation_module" "mymodule" {
  name                    = "mymodule"
  resource_group_name     = azurerm_resource_group.example.name
  automation_account_name = azurerm_automation_account.example.name

  module_link {
    uri = "https://somewhere/mymodule.1.0.0.zip"
  }
}
</code></pre></div></div>

<h2 id="code">Code</h2>

<p><em>Show me the code already! Jump to <a href="#download">download</a></em></p>

<p>In the following I’m using Terraform and the azurerm provider to create the resources needed. Nothing very complicated but a few requirements to be aware of.</p>

<h3 id="requirements">Requirements</h3>
<h4 id="authentication">Authentication</h4>

<p>For the authentication to Azure I’m using a Service Principal Name (SPN) which has the appropriate permissions to go and do things on my Subscription.</p>

<p>As an example, you can create an SPN and grant it “Contributor” permissions using the following Az Cli commands:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>az ad sp create-for-rbac <span class="nt">--role</span><span class="o">=</span><span class="s2">"Contributor"</span> <span class="nt">--scopes</span><span class="o">=</span><span class="s2">"/subscriptions/&lt;subID&gt;"</span>
</code></pre></div></div>

<p>The output will look like this</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>{
  "appId": "&lt;Secret Stuff&gt;",         # client_id
  "displayName": "azure-cli-2019-05-26-21-21-54",
  "name": "http://azure-cli-2019-05-26-21-21-54",
  "password": "&lt;Secret Stuff&gt;",      # client_secret
  "tenant": "&lt;Secret Stuff&gt;"         # tenant_id
}
</code></pre></div></div>

<h4 id="create-the-secretstfvarsjson-file">Create the secrets.tfvars.json file</h4>

<p>From the previous output, I’m creating a file called <code class="language-plaintext highlighter-rouge">secrets.tfvars.json</code>.
This file will be passed to Terraform at runtime.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>{
  "appId": "&lt;spn app id&gt;",
  "password": "&lt;spn password&gt;",
  "tenant": "&lt;tenant id&gt;",
  "subscription_id": "&lt;sub id&gt;"
}
</code></pre></div></div>

<p>If you intend to run terraform in a CI/CD system, you might choose to store these values in a Vault, pull them at runtime and <a href="https://www.terraform.io/docs/providers/azurerm/index.html#authenticating-to-azure">set environment variables recognized by Terraform</a>.</p>

<h3 id="usage">Usage</h3>

<p>Once the requirements described above are in place, we can proceed with the following commands:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Navigate to directory containing the configurations</span>

<span class="c"># Initial configuration/get dependencies</span>
terraform init

<span class="c"># Plan and Apply our changes</span>
terraform plan <span class="nt">-var-file</span> <span class="s2">"secrets.tfvars.json"</span> <span class="nt">-out</span> automation.tfplan
terraform apply <span class="s2">"automation.tfplan"</span>

<span class="c"># Destroying the resources</span>
terraform plan <span class="nt">-var-file</span> <span class="s2">"secrets.tfvars.json"</span> <span class="nt">-out</span> automation.tfplan <span class="nt">-destroy</span>
terraform apply <span class="s2">"automation.tfplan"</span>
</code></pre></div></div>

<h4 id="terraform-plan">terraform plan</h4>

<p><img src="../../images/2020/2020-08-02/azure_automation_account-upload_module-terraform_plan2.png" alt="" /></p>

<h4 id="terraform-apply">terraform apply</h4>

<p><img src="../../images/2020/2020-08-02/azure_automation_account-upload_module-terraform_apply.png" alt="" /></p>

<h2 id="download">Download</h2>

<p>The code is available <a href="https://github.com/lazywinadmin/terraform/tree/master/azure/automationaccount/upload_psmodule">on Github</a> on my <code class="language-plaintext highlighter-rouge">terraform</code> repository.</p>

<h2 id="resources">Resources</h2>

<ul>
  <li><a href="https://www.terraform.io/docs/providers/azurerm/index.html#authenticating-to-azure">Terraform -  Authenticating to Azure</a></li>
  <li><a href="https://www.terraform.io/docs/providers/azurerm/r/automation_module.html">Terraform -  azurerm_automation_module resource</a></li>
  <li><a href="https://docs.microsoft.com/en-us/rest/api/automation/module/createorupdate#contentlink">Microsoft - Management API - Automation/Module</a></li>
</ul>]]></content><author><name>François-Xavier Cat</name></author><category term="azure" /><category term="terraform" /><category term="automationaccount" /><category term="powershell" /><summary type="html"><![CDATA[This article demonstrates how to use Terraform to upload a local PowerShell module to an Azure Storage Account and importing it to an Automation Account using a temporary secret link]]></summary></entry><entry><title type="html">PowerShell/Azure - Update App Service Access Restriction IP Rules</title><link href="https://lazywinadmin.github.io/2020/07/azure_appservice_restrictions_iprules.html" rel="alternate" type="text/html" title="PowerShell/Azure - Update App Service Access Restriction IP Rules" /><published>2020-07-05T00:00:00+00:00</published><updated>2020-07-05T00:00:00+00:00</updated><id>https://lazywinadmin.github.io/2020/07/azure_appservice_restrictions_iprules</id><content type="html" xml:base="https://lazywinadmin.github.io/2020/07/azure_appservice_restrictions_iprules.html"><![CDATA[<p class="notice--info"><strong>Tested on</strong> PowerShell 7.0.0 on Ubuntu 18.04</p>

<p>I recently helped out a friend who needed to configure restrictive IP Address Rules on an Azure App Service Service using PowerShell. The goal was to be able to set IP Address Rules but also overwrite anything currently setup.</p>

<p>Azure App Services are publicly accessible by default via <code class="language-plaintext highlighter-rouge">your_app_name.azurewebsites.net</code> or <code class="language-plaintext highlighter-rouge">your_app_name.scm.azurewebsites.net</code>. You might want to restrict the access to specific IP CIDR or force clients to access your app via different routes, examples: Application Gateway or Firewall.</p>

<p>In the Azure Portal, the IP Address Rules can be set under the <strong>Networking</strong>/<strong>Access Restrictions</strong> properties of the App Service.</p>

<p><img src="../../images/2020/2020-07-05-azure_appservice_restrictions_iprules/azureportal-iprules.png" alt="" /></p>

<p>I would usually recommend using something like Terraform for these types of scenarios where you want to track the state of your resources. However for this particular example we wanted to perform the task using a PowerShell script.</p>

<p>We came up with the following approach where we used a CSV file as a source of truth and let PowerShell do the rest by appending/or/overwriting the IP Address Rules specified in the file.</p>

<p>Here is an example of content for the CSV File:</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>"ipAddress","action","priority","name","description","tag"
"120.3.2.0/20","Allow","100","test ip1","test ip1","Default"
"120.3.3.0/20","Allow","100","test ip4","test ip4","Default"
</code></pre></div></div>

<h1 id="usage">Usage</h1>

<p>If you look at the Comment-based help of the script, there are a few mandatory parameters: <code class="language-plaintext highlighter-rouge">-ResourceGroupName</code>, <code class="language-plaintext highlighter-rouge">-AppServiceName</code>, <code class="language-plaintext highlighter-rouge">Subscription</code> which help defined the scope/target of the change. <code class="language-plaintext highlighter-rouge">Path</code> to provide the CSV file which contain the IP Address Rules.</p>

<p><code class="language-plaintext highlighter-rouge">Overwrite</code> specifies that you want to erase all the existing rules. When not specified, the script assume you just want to append new rules. It will also check if the IP/CIDR is already present before appending, if already present it will skip the rule.</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Append ip rules not already present.</span><span class="w">
</span><span class="c"># The script only checks if the IP/CIDR is present,</span><span class="w">
</span><span class="c">#  it does not check the other properties</span><span class="w">
</span><span class="nv">$Splatting</span><span class="o">=</span><span class="p">@{</span><span class="w">
    </span><span class="nx">ResourceGroupName</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"MyRG"</span><span class="w">
    </span><span class="nx">AppServiceName</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"MyApp"</span><span class="w">
    </span><span class="nx">SubscriptionId</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">'&lt;GUID&gt;'</span><span class="w">
    </span><span class="nx">Path</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="err">./</span><span class="nx">source</span><span class="err">.</span><span class="nx">csv</span><span class="w">
    </span><span class="nx">verbose</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="bp">$true</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="n">AppService-Update_RestrictionIP.ps1</span><span class="w"> </span><span class="err">@</span><span class="nx">Splatting</span><span class="w">

</span><span class="c"># Erase all existing rules and append</span><span class="w">
</span><span class="c">#  the ones specified in the CSV file</span><span class="w">
</span><span class="nv">$Splatting</span><span class="o">=</span><span class="p">@{</span><span class="w">
    </span><span class="nx">ResourceGroupName</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"MyRG"</span><span class="w">
    </span><span class="nx">AppServiceName</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"MyApp"</span><span class="w">
    </span><span class="nx">SubscriptionId</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">'&lt;GUID&gt;'</span><span class="w">
    </span><span class="nx">Path</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="err">./</span><span class="nx">source</span><span class="err">.</span><span class="nx">csv</span><span class="w">
    </span><span class="nx">verbose</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="bp">$true</span><span class="w">
    </span><span class="nx">overwrite</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="bp">$true</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="n">AppService-Update_RestrictionIP.ps1</span><span class="w"> </span><span class="err">@</span><span class="nx">Splatting</span><span class="w">
</span></code></pre></div></div>

<p>Script in action:</p>

<p><img src="../../images/2020/2020-07-05-azure_appservice_restrictions_iprules/script_output-appservice-update_restrictionip.png" alt="" /></p>

<h2 id="download">Download</h2>

<p><a href="https://github.com/lazywinadmin/PowerShell/tree/master/AZURE-AppService-Update_RestrictionIP">Script available on Github</a>.</p>

<h2 id="resources">Resources</h2>

<ul>
  <li><a href="https://docs.microsoft.com/en-us/azure/app-service/app-service-ip-restrictions">Microsoft Docs - Azure App Service access restrictions</a></li>
  <li><a href="https://docs.microsoft.com/en-us/azure/web-application-firewall/ag/ag-overview">Microsoft Docs - Azure Web Application Firewall on Azure Application Gateway</a></li>
</ul>]]></content><author><name>François-Xavier Cat</name></author><category term="powershell" /><category term="azure" /><summary type="html"><![CDATA[Quick PowerShell script to append or overwrite the Network IP Rules restriction of a App Service]]></summary></entry><entry><title type="html">Microsoft MVP 2020-2021</title><link href="https://lazywinadmin.github.io/2020/07/Microsoft_MVP_2020-2021.html" rel="alternate" type="text/html" title="Microsoft MVP 2020-2021" /><published>2020-07-01T00:00:00+00:00</published><updated>2020-07-01T00:00:00+00:00</updated><id>https://lazywinadmin.github.io/2020/07/Microsoft_MVP_2020-2021</id><content type="html" xml:base="https://lazywinadmin.github.io/2020/07/Microsoft_MVP_2020-2021.html"><![CDATA[<p>It’s that time of the year! July 1st is the renewal date for all the Microsoft Most Valuable Professionals (MVPs).</p>

<p>I feel honored, proud and pleased to receive the Microsoft MVP award for another year for the category <b>Cloud and DataCenter Management MVP</b>!</p>

<blockquote>
  <p>Dear Francois-Xavier,<br />
We’re once again pleased to present you with the 2020-2021 Microsoft Most Valuable Professional (MVP) award in recognition of your exceptional technical community leadership. We appreciate your outstanding contributions in the following technical communities during the past year:<br />
   Cloud and Datacenter Management</p>
</blockquote>

<p><strong>I want to thank everyone for their support, the incredible Community and the teams at Microsoft</strong> :bow:</p>

<p><img src="../images/2020/../../../images/2020/2020-07-01-Microsoft_MVP_2020-2021/mvp2021.png" alt="" /></p>

<p>In the last year most my contributions were around:</p>

<ul>
  <li><a href="https://github.com/lazywinadmin">Contributions to Open Source projects</a></li>
  <li><a href="https://frpsug.com/meet">French PowerShell user group</a></li>
</ul>

<p>See my Microsoft MVP profile <a href="https://mvp.microsoft.com/en-us/PublicProfile/5000475" target="_blank">here</a>.</p>

<p><img src="/images/2018/2018-07-01-Microsoft_MVP_2018-2019/MicrosoftMVPLogo.png" alt="image-center" /></p>]]></content><author><name>François-Xavier Cat</name></author><category term="achievement" /><category term="cloud and datacenter management" /><category term="microsoft" /><category term="mvp" /><category term="powershell" /><summary type="html"><![CDATA[It is a great pleasure and honor to receive the Microsoft MVP award for another year]]></summary></entry><entry><title type="html">Offline Domain Join using PowerShell and c#</title><link href="https://lazywinadmin.github.io/2020/06/powershell-offline_domain_join_csharp.html" rel="alternate" type="text/html" title="Offline Domain Join using PowerShell and c#" /><published>2020-06-30T00:00:00+00:00</published><updated>2020-06-30T00:00:00+00:00</updated><id>https://lazywinadmin.github.io/2020/06/powershell-offline_domain_join_csharp</id><content type="html" xml:base="https://lazywinadmin.github.io/2020/06/powershell-offline_domain_join_csharp.html"><![CDATA[<p class="notice--info"><strong>Last update:</strong> 2020/07/09 - High level diagram of the ConfigMgr implementation</p>

<p>I wrote on Offline Domain Join (ODJ) a while back where I used <a href="../2016/2016-07-01-Offline_Domain_Join_-_Recreating_the_Blob_file_using_PowerShell.md">PowerShell to rebuild a “djoin-compatible” file</a> from the <code class="language-plaintext highlighter-rouge">djoin.exe</code> output in a format that djoin would accept. In my scenario at the time, I couldn’t pass the file generated by djoin directly.</p>

<p>What is the <code class="language-plaintext highlighter-rouge">djoin.exe</code> you may ask ? this is a tool provided by Microsoft that allow you to join a machine to an Active Directory domain without network connectivity. The operation happens in two phases: First you run djoin on a machine connected to Active Directory which output a very long string of text (or a file directly with that string), in a second part you provide this file to djoin on the machine that need to be joined to complete the operation.</p>

<p>The article I wrote at that time was focused on the second phase. I was still using djoin.exe for the first phase.</p>

<p><img src="/images/2020/2020-06-30-powershell-offline_domain_join_csharp/ad-offline_domain_join.png" alt="" /></p>

<h2 id="not-fast-enough">Not fast enough…</h2>

<p>More recently, I was facing another djoin problematic scenario.</p>

<p>I had to create an automation to stage new device(s) in different systems, including SCCM and make the device ready to boot from SCCM OSD and start applying an OS + Configuration.</p>

<p>The high-level flow looked like this:</p>

<ol>
  <li>Receive a request via Rest API with some parameters (ComputerName, MacAddress, Owner, Location, …)</li>
  <li>Invoke PowerShell Script to Stage the new device
    <ol>
      <li>Register new device into custom systems (inventory system, monitoring system, asset management system…)</li>
      <li>Create a SCCM Device Association using the ComputerName, MacAddress, …</li>
      <li>Append the SCCM Device to the Collection <code class="language-plaintext highlighter-rouge">ABC00001</code> where the OS desired is advertised</li>
      <li>Wait for the machine to be queryable from the <code class="language-plaintext highlighter-rouge">SMS00001</code> default collection. If the new device boot but this collection is not refreshed, it won’t pick up the SCCM Operating System Deployment (OSD) assigned on the collection <code class="language-plaintext highlighter-rouge">ABC00001</code>.</li>
      <li>Perform the first phase of Offline Domain Join using <code class="language-plaintext highlighter-rouge">djoin.exe</code> under a different account that hold the proper entitlements,</li>
      <li>Create SCCM Device Variables to pass different values consumed during the SCCM OSD</li>
    </ol>
  </li>
  <li>New machine boot for the first time, pickup DHCP Information/IP</li>
  <li>New machine start SCCM OSD and get all the values from SCCM Device Variables</li>
  <li>Apply OS and Configuration</li>
</ol>

<p>Here is an high-level flow:</p>

<p><img src="/images/2020/2020-06-30-powershell-offline_domain_join_csharp/odj-automation2.png" alt="" /></p>

<p>This was working really well, the whole <strong>Stage</strong> process was taking 3-5 minutes. However one of the tasks that was costly in time was the ODJ part. This automation needed to run very frequently so it started to become a bottleneck for the entire workflow where hundreds machines needed to be deployed at the same time.</p>

<p>Djoin was slow mostly because:</p>
<ul>
  <li>The script had to do a bunch of pre-work before invoking djoin, the new device was not actually on the network yet. Example of task: Find the closest Domain Controllers (DC) to the new machine based on the New computer Location information or IPAddress. In some edge case if only one DC/RODC was in the location, the code had to look for closest neighbor site based on site link costs.</li>
  <li>PowerShell was invoked under different context, using an account that has the permissions to run djoin.</li>
  <li>The tool I was using was spawning a new process (when doing RunAs) which took some extra precious seconds</li>
  <li>Invoke Djoin against a Domain Controller close to new machine</li>
  <li>Capture the Output and parse it</li>
  <li>Cherry on the cake PowerShell 2.0 (yep…) had to be used, that came with some other limitations.</li>
</ul>

<p>On a side note, in this scenario the second part of the djoin was handled by <code class="language-plaintext highlighter-rouge">unattend.xml</code> file inside the SCCM Task Sequence, the djoin string was appended to it.</p>

<p>Alternatively you could still use the approach shown in <a href="../2016/2016-07-01-Offline_Domain_Join_-_Recreating_the_Blob_file_using_PowerShell.md">my previous ODJ blogpost</a>.</p>

<h2 id="solution-using-c-and-powershell">Solution using c# and PowerShell</h2>

<p>I spent sometimes looking for a solution that could expedite the ODJ step while using the entitled AD account. I tested different approaches but ultimately ended up using a combination of PowerShell and c#</p>

<p>While investigating on P/Invoke around <a href="https://www.pinvoke.net/default.aspx/netapi32/NetJoinDomain.html">[NetJoinDomain]</a>, <a href="https://www.pinvoke.net/default.aspx/netapi32.NetGetJoinInformation">[NetGetJoinInformation]</a> methods, I found the following <a href="https://gist.github.com/Ryan2065/79838b78643d2311d60cb6147e3b87bf">code snippet</a> from <a href="https://twitter.com/EphingPosh">Ryan Ephgrave</a> which was using PowerShell/c# to join a Nano Server during WinPE.</p>

<p>For my need, this was working great during my initial tests on W2008R2 but I had to update the code a bit to make it work on Windows Server 2012/2016+ versions.</p>

<ul>
  <li>Window Client 7/ Windows 2008 are using the <a href="https://docs.microsoft.com/en-us/windows/desktop/api/lmjoin/nf-lmjoin-netprovisioncomputeraccount">NetProvisionComputerAccount function</a></li>
  <li>Windows Client 8+/ Windows Server 2012+ are using the <a href="https://docs.microsoft.com/en-us/windows/desktop/api/lmjoin/nf-lmjoin-netcreateprovisioningpackage">NetCreateProvisioningPackage function</a></li>
</ul>

<p>Overall this approach saved a lot of time and run in seconds compared <code class="language-plaintext highlighter-rouge">djoin.exe</code>.</p>

<h2 id="usage">Usage</h2>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Define Credentials to use</span><span class="w">
</span><span class="nv">$MyAccount</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">'MyDomain\MyAccount'</span><span class="w">
</span><span class="nv">$Passw</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">ConvertTo-SecureString</span><span class="w"> </span><span class="nt">-String</span><span class="w"> </span><span class="s1">'&lt;SecretPassword&gt;'</span><span class="w"> </span><span class="nt">-AsplainText</span><span class="w"> </span><span class="nt">-Force</span><span class="w">
</span><span class="nv">$Credential</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">New-Object</span><span class="w"> </span><span class="nt">-TypeName</span><span class="w"> </span><span class="nx">System.Management.Automation.PSCredential</span><span class="w"> </span><span class="nt">-ArgumentList</span><span class="w"> </span><span class="p">(</span><span class="nv">$MyAccount</span><span class="p">,</span><span class="nv">$Passw</span><span class="p">)</span><span class="w">

</span><span class="c"># Define Parameters</span><span class="w">
</span><span class="nv">$Splatting</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">@{</span><span class="w">
    </span><span class="nx">domain</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">'mydomain.com'</span><span class="w">
    </span><span class="nx">machinename</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">'testmachine'</span><span class="w">
    </span><span class="nx">machineaccountou</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">'OU=test,DC=mydomain,DC=com'</span><span class="w">
    </span><span class="nx">dcname</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">'dc01.mydomain.com'</span><span class="w">
</span><span class="p">}</span><span class="w">

</span><span class="c"># Invoke the Offline Domain Join</span><span class="w">
</span><span class="n">New-ADDomainJoin</span><span class="w"> </span><span class="nt">-Credential</span><span class="w"> </span><span class="nv">$credential</span><span class="w"> </span><span class="err">@</span><span class="nx">splatting</span><span class="w">
</span></code></pre></div></div>

<p>This will create an object in active directory and output the blob needed to join the machine offline.</p>

<h2 id="download">Download</h2>

<p>The <a href="https://github.com/lazywinadmin/PowerShell/blob/master/AD-COMPUTER-New-ADDomainJoin/New-ADDomainJoin.ps1">code is available on GitHub</a>.</p>

<p>Hope this helps :-)</p>]]></content><author><name>François-Xavier Cat</name></author><category term="active directory" /><category term="djoin" /><category term="offline domain join" /><category term="powershell" /><category term="sccm" /><summary type="html"><![CDATA[Last update: 2020/07/09 - High level diagram of the ConfigMgr implementation]]></summary></entry><entry><title type="html">PowerShell 7 - What’s new ?</title><link href="https://lazywinadmin.github.io/2020/03/powershell7_whatsnew.html" rel="alternate" type="text/html" title="PowerShell 7 - What’s new ?" /><published>2020-03-23T00:00:00+00:00</published><updated>2020-03-23T00:00:00+00:00</updated><id>https://lazywinadmin.github.io/2020/03/powershell7_whatsnew</id><content type="html" xml:base="https://lazywinadmin.github.io/2020/03/powershell7_whatsnew.html"><![CDATA[<p class="notice--info"><strong>Tested on</strong> PowerShell 7.0.0 on Ubuntu 18.04</p>

<p>Last week I gave a <a href="https://www.youtube.com/watch?v=nqb6s0PYfM0">presentation on “PowerShell 7 - What’s new ?”</a> at the French PowerShell User Group.</p>

<p>I wanted to highlight some of the new features mentioned during this presentation and also some discovered since the release.</p>

<h2 id="powershell-7">PowerShell 7</h2>

<h3 id="what-changed-">What changed ?</h3>

<ul>
  <li>PowerShell 7 (<code class="language-plaintext highlighter-rouge">Core</code> has been removed from the name)</li>
  <li>First Long Term Servicing (LTS) release</li>
  <li>PowerShell 7 is based on .NET Core 3.1</li>
  <li>Supported Operating Systems:
    <ul>
      <li>Windows 8.1, and 10</li>
      <li>Windows Server 2012, 2012 R2, 2016, and 2019</li>
      <li>macOS 10.13+</li>
      <li>Red Hat Enterprise Linux (RHEL) / CentOS 7</li>
      <li>Fedora 30+</li>
      <li>Debian 9</li>
      <li>Ubuntu LTS 16.04+</li>
      <li>Alpine Linux 3.8+</li>
      <li>ARM32 and ARM64 flavors of Debian, Ubuntu, and ARM64 Alpine Linux.</li>
    </ul>
  </li>
  <li>Unsupported Operating System (where PowerShell 7 works)
    <ul>
      <li>Kali Linux</li>
    </ul>
  </li>
</ul>

<h2 id="installation">Installation</h2>

<p>PowerShell can be installed using different methods:</p>

<ul>
  <li>Windows: <code class="language-plaintext highlighter-rouge">iex "&amp; { $(irm https://aka.ms/install-powershell.ps1) } -UseMSI"'</code></li>
  <li>Linux: <code class="language-plaintext highlighter-rouge">'wget https://aka.ms/install-powershell.sh; sudo bash install-powershell.sh; rm install-powershell.sh'</code></li>
  <li>MacOS: <code class="language-plaintext highlighter-rouge">brew cask install powershell</code></li>
  <li>Using the .NET SDK: <code class="language-plaintext highlighter-rouge">dotnet tool install --global PowerShell</code></li>
</ul>

<p>Other methods are available <a href="https://github.com/PowerShell/PowerShell/releases/tag/v7.0.0">here</a>.</p>

<h2 id="foreach-object--parallel">ForEach-Object -Parallel</h2>

<h3 id="what-is-it-">What is it ?</h3>

<ul>
  <li>Brings built-in parallelism mechanism (leveraging runspaces)
    <ul>
      <li>Similar to what some other modules/scripts can already do (ThreadJob, PoshRSJob, Invoke-Parallel)</li>
    </ul>
  </li>
  <li>Control number of Threads started using <code class="language-plaintext highlighter-rouge">-ThrottleLimit</code></li>
  <li>Control TimeOut of each Thread using <code class="language-plaintext highlighter-rouge">-TimeoutSeconds</code></li>
  <li>Can be managed by PowerShell Jobs using <code class="language-plaintext highlighter-rouge">-AsJob</code></li>
  <li>Does not apply to <code class="language-plaintext highlighter-rouge">.ForEach()</code> method or <code class="language-plaintext highlighter-rouge">ForEach</code> statement/loop.</li>
</ul>

<p>Here is the syntax of the parameter set</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">ForEach-Object</span><span class="w"> </span><span class="nt">-Parallel</span><span class="w"> </span><span class="err">&lt;</span><span class="nx">scriptblock</span><span class="err">&gt;</span><span class="w">
    </span><span class="p">[</span><span class="nt">-InputObject</span><span class="w"> </span><span class="err">&lt;</span><span class="n">psobject</span><span class="err">&gt;</span><span class="p">]</span><span class="w">
    </span><span class="p">[</span><span class="nt">-ThrottleLimit</span><span class="w"> </span><span class="err">&lt;</span><span class="n">int</span><span class="err">&gt;</span><span class="p">]</span><span class="w">
    </span><span class="p">[</span><span class="nt">-TimeoutSeconds</span><span class="w"> </span><span class="err">&lt;</span><span class="n">int</span><span class="err">&gt;</span><span class="p">]</span><span class="w">
    </span><span class="p">[</span><span class="nt">-AsJob</span><span class="p">]</span><span class="w">
    </span><span class="p">[</span><span class="nt">-WhatIf</span><span class="p">]</span><span class="w"> </span><span class="p">[</span><span class="nt">-Confirm</span><span class="p">]</span><span class="w"> </span><span class="p">[</span><span class="err">&lt;</span><span class="n">CommonParameters</span><span class="err">&gt;</span><span class="p">]</span><span class="w">
</span></code></pre></div></div>

<h3 id="usage">Usage</h3>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="mi">1</span><span class="o">..</span><span class="mi">5</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Foreach-Object</span><span class="w"> </span><span class="nt">-Parallel</span><span class="w"> </span><span class="p">{</span><span class="bp">$_</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p><img src="/images/2020/2020-03-23-powershell7_whatsnew/Terminal_001.png" alt="image-center" class="align-center" /></p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Using Throttle (default is 5)</span><span class="w">
</span><span class="c">#  Here I'm using Get-Runspace to show the number of live runspaces</span><span class="w">
</span><span class="mi">1</span><span class="o">..</span><span class="mi">30</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Foreach-Object</span><span class="w"> </span><span class="nt">-Parallel</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="err">$</span><span class="p">((</span><span class="n">Get-Runspace</span><span class="p">)</span><span class="o">.</span><span class="nf">count</span><span class="p">);</span><span class="n">Start-Sleep</span><span class="w"> </span><span class="nt">-sec</span><span class="w"> </span><span class="nx">3</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="nt">-ThrottleLimit</span><span class="w"> </span><span class="mi">20</span><span class="w">
</span></code></pre></div></div>

<p>Let’s set a maximum of 20 threads and try to hit the limit.</p>

<p><img src="/images/2020/2020-03-23-powershell7_whatsnew/Terminal_002.png" alt="image-center" class="align-center" /></p>

<p>We can also show the current Process ID, Thread ID and RunSpace ID by executing the following.</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Show Runspace/Thread ID</span><span class="w">
</span><span class="mi">1</span><span class="o">..</span><span class="mi">10</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Foreach-Object</span><span class="w"> </span><span class="nt">-Parallel</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="s2">"</span><span class="bp">$_</span><span class="s2"> - PID: </span><span class="bp">$PID</span><span class="s2"> - Thread </span><span class="si">$(</span><span class="p">[</span><span class="n">System.Threading.Thread</span><span class="p">]::</span><span class="n">CurrentThread.ManagedThreadID</span><span class="p">)</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">Runspace</span><span class="w"> </span><span class="nx">ID</span><span class="w"> </span><span class="err">$</span><span class="p">(([</span><span class="n">System.Management.Automation.Runspaces.Runspace</span><span class="p">]::</span><span class="n">DefaultRunSpace</span><span class="p">)</span><span class="o">.</span><span class="nf">id</span><span class="si">)</span><span class="s2">"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="c"># Difference between Process/Thread/Runspace:</span><span class="w">
</span><span class="c">#   https://stackoverflow.com/questions/54503232/process-vs-instance-vs-runspace-in-powershell</span><span class="w">
</span><span class="c"># Process   : Program that run an instruction set</span><span class="w">
</span><span class="c"># Thread    : Single instruction</span><span class="w">
</span><span class="c"># Runspace  : New PowerShell engine under the same Program (process)</span><span class="w">

</span><span class="c"># Worth noting.. Invoke-Command already have its own parallelism systems</span><span class="w">
</span><span class="c"># so not always a good things to combine the two</span><span class="w">
</span></code></pre></div></div>

<p><img src="/images/2020/2020-03-23-powershell7_whatsnew/Terminal_004.png" alt="image-center" class="align-center" /></p>

<h3 id="performance">Performance</h3>

<p>Is <code class="language-plaintext highlighter-rouge">-Parallel</code> really faster than the default <code class="language-plaintext highlighter-rouge">-Process</code> parameter ? Well it depends on your dataset. Parallel does not mean faster.</p>

<p>Let’s compare:</p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">-Process</code></li>
  <li><code class="language-plaintext highlighter-rouge">-Parallel</code> with the default 5 threads</li>
  <li><code class="language-plaintext highlighter-rouge">-Parallel</code> with 20 threads</li>
</ul>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Fast (Foreach-Object -Process )</span><span class="w">
</span><span class="p">(</span><span class="n">Measure-Command</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="mi">1</span><span class="o">..</span><span class="mi">100</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Foreach-Object</span><span class="w"> </span><span class="nt">-Process</span><span class="w"> </span><span class="p">{</span><span class="bp">$_</span><span class="p">}</span><span class="w"> </span><span class="p">})</span><span class="o">.</span><span class="nf">TotalMilliseconds</span><span class="w">
</span><span class="c"># Slower (Foreach-Object -Parallel with default 5 threads limit )</span><span class="w">
</span><span class="p">(</span><span class="n">Measure-Command</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="mi">1</span><span class="o">..</span><span class="mi">100</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Foreach-Object</span><span class="w"> </span><span class="nt">-Parallel</span><span class="w"> </span><span class="p">{</span><span class="bp">$_</span><span class="p">}</span><span class="w"> </span><span class="p">})</span><span class="o">.</span><span class="nf">TotalMilliseconds</span><span class="w">
</span><span class="c"># Slower (Foreach-Object -Parallel with default 20 threads limit )</span><span class="w">
</span><span class="p">(</span><span class="n">Measure-Command</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="mi">1</span><span class="o">..</span><span class="mi">100</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Foreach-Object</span><span class="w"> </span><span class="nt">-Parallel</span><span class="w"> </span><span class="p">{</span><span class="bp">$_</span><span class="p">}</span><span class="w"> </span><span class="nt">-ThrottleLimit</span><span class="w"> </span><span class="mi">20</span><span class="w"> </span><span class="p">})</span><span class="o">.</span><span class="nf">TotalMilliseconds</span><span class="w">
</span></code></pre></div></div>

<p>As we can see <code class="language-plaintext highlighter-rouge">-Process</code> is actually faster in this scenario.</p>

<p><img src="/images/2020/2020-03-23-powershell7_whatsnew/Terminal_005.png" alt="image-center" class="align-center" /></p>

<p>Now let’s add a 1 second sleep to make the thread last a bit longer.</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># -Process</span><span class="w">
</span><span class="p">(</span><span class="n">Measure-Command</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="mi">1</span><span class="o">..</span><span class="mi">10</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Foreach-Object</span><span class="w"> </span><span class="nt">-Process</span><span class="w"> </span><span class="p">{</span><span class="n">Start-Sleep</span><span class="w"> </span><span class="nt">-Seconds</span><span class="w"> </span><span class="nx">1</span><span class="p">}</span><span class="w"> </span><span class="p">})</span><span class="o">.</span><span class="nf">TotalMilliseconds</span><span class="w">
</span><span class="c"># -Parallel (5 threads limit)</span><span class="w">
</span><span class="p">(</span><span class="n">Measure-Command</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="mi">1</span><span class="o">..</span><span class="mi">10</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Foreach-Object</span><span class="w"> </span><span class="nt">-Parallel</span><span class="w"> </span><span class="p">{</span><span class="n">Start-Sleep</span><span class="w"> </span><span class="nt">-Seconds</span><span class="w"> </span><span class="nx">1</span><span class="p">}</span><span class="w"> </span><span class="p">})</span><span class="o">.</span><span class="nf">TotalMilliseconds</span><span class="w">
</span><span class="c"># -Parallel (10 threads limit)</span><span class="w">
</span><span class="p">(</span><span class="n">Measure-Command</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="mi">1</span><span class="o">..</span><span class="mi">10</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Foreach-Object</span><span class="w"> </span><span class="nt">-Parallel</span><span class="w"> </span><span class="p">{</span><span class="n">Start-Sleep</span><span class="w"> </span><span class="nt">-Seconds</span><span class="w"> </span><span class="nx">1</span><span class="p">}</span><span class="nt">-ThrottleLimit</span><span class="w"> </span><span class="mi">10</span><span class="w"> </span><span class="p">})</span><span class="o">.</span><span class="nf">TotalMilliseconds</span><span class="w">
</span></code></pre></div></div>

<p>Now we can see the benefits of using this parameter when some tasks can take longer.</p>

<p><img src="/images/2020/2020-03-23-powershell7_whatsnew/Terminal_006.png" alt="image-center" class="align-center" /></p>

<h3 id="passing-data-to-the-runspaces">Passing data to the runspaces</h3>

<p>Passing data to each runspace can be done by specify the <code class="language-plaintext highlighter-rouge">$using:</code> scope in front of your variable name. Similar to what you have to do for remote variable.</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Passing data to the runspaces</span><span class="w">
</span><span class="nv">$Message</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Output:"</span><span class="w">

</span><span class="mi">1</span><span class="o">..</span><span class="mi">8</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">ForEach-Object</span><span class="w"> </span><span class="nt">-Parallel</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="s2">"</span><span class="nv">$</span><span class="nn">Using</span><span class="p">:</span><span class="nv">Message</span><span class="s2"> </span><span class="bp">$_</span><span class="s2">"</span><span class="w">
    </span><span class="n">Start-Sleep</span><span class="w"> </span><span class="nx">1</span><span class="w">
</span><span class="p">}</span><span class="w"> </span><span class="nt">-ThrottleLimit</span><span class="w"> </span><span class="mi">4</span><span class="w">
</span></code></pre></div></div>

<p><img src="/images/2020/2020-03-23-powershell7_whatsnew/Terminal_007.png" alt="image-center" class="align-center" /></p>

<h3 id="asjob">AsJob</h3>

<p><code class="language-plaintext highlighter-rouge">-Parallel</code> also supports jobs, where you can choose to have a job object returned instead of having results written to the console.</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Run in parallel as a PowerShell job</span><span class="w">
</span><span class="mi">1</span><span class="o">..</span><span class="mi">10</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">ForEach-Object</span><span class="w"> </span><span class="nt">-Parallel</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="s2">"Output: </span><span class="bp">$_</span><span class="s2">"</span><span class="w">
    </span><span class="n">Start-Sleep</span><span class="w"> </span><span class="nx">1</span><span class="w">
</span><span class="p">}</span><span class="w"> </span><span class="nt">-ThrottleLimit</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="nt">-AsJob</span><span class="w"> </span><span class="o">|</span><span class="w">
</span><span class="n">Receive-Job</span><span class="w"> </span><span class="nt">-Wait</span><span class="w">
</span></code></pre></div></div>

<p><img src="/images/2020/2020-03-23-powershell7_whatsnew/Terminal_008.png" alt="image-center" class="align-center" /></p>

<h3 id="storing-data-from-multiple-runspaces">Storing data from multiple runspaces</h3>

<p>If you are gathering information from different locations, you might want to have a single place to store the data.</p>

<p>One way is to use the dictionnary object <code class="language-plaintext highlighter-rouge">[System.Collections.Concurrent.ConcurrentDictionary]</code>, example:</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Create dictionary</span><span class="w">
</span><span class="nv">$threadSafeDictionary</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[</span><span class="n">System.Collections.Concurrent.ConcurrentDictionary</span><span class="p">[</span><span class="n">string</span><span class="p">,</span><span class="n">object</span><span class="p">]]::</span><span class="n">new</span><span class="p">()</span><span class="w">

</span><span class="c"># Retrieve files</span><span class="w">
</span><span class="nv">$Files</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"~\code\Presentations\20200310-PowerShell7-whats_new\"</span><span class="w">
</span><span class="n">Get-Childitem</span><span class="w"> </span><span class="s2">"</span><span class="nv">$Files</span><span class="s2">*.txt"</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">ForEach-Object</span><span class="w"> </span><span class="nt">-Parallel</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="c"># Retrieve mention of a the word 'powershell' in txt files</span><span class="w">
    </span><span class="nv">$result</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="n">Get-Content</span><span class="w"> </span><span class="bp">$_</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Select-string</span><span class="w"> </span><span class="nx">powershell</span><span class="p">)</span><span class="o">.</span><span class="nf">linenumber</span><span class="w">

    </span><span class="c"># Append information to dictionary outside the runspace</span><span class="w">
    </span><span class="nv">$dict</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nv">$</span><span class="nn">using</span><span class="p">:</span><span class="nv">threadSafeDictionary</span><span class="w">
    </span><span class="nv">$dict</span><span class="o">.</span><span class="nf">TryAdd</span><span class="p">(</span><span class="bp">$_</span><span class="o">.</span><span class="nf">basename</span><span class="p">,</span><span class="w"> </span><span class="nv">$result</span><span class="p">)</span><span class="w">
</span><span class="p">}</span><span class="w">

</span><span class="c"># Show content</span><span class="w">
</span><span class="nv">$threadSafeDictionary</span><span class="w">

</span><span class="nv">$threadSafeDictionary</span><span class="o">.</span><span class="nf">Keys</span><span class="w">
</span><span class="nv">$threadSafeDictionary</span><span class="o">.</span><span class="nf">Values</span><span class="w">
</span><span class="nv">$threadSafeDictionary</span><span class="p">[</span><span class="s2">"github"</span><span class="p">]</span><span class="w">
</span></code></pre></div></div>

<p><img src="/images/2020/2020-03-23-powershell7_whatsnew/Terminal_011.png" alt="image-center" class="align-center" /></p>

<h3 id="set-a-timeout">Set a timeout</h3>

<p>You might want to set a “Time to live” limit in second(s) on each of your Runspaces. This can be done using the <code class="language-plaintext highlighter-rouge">-TimeoutSeconds</code> parameter, example:</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Set timeout</span><span class="w">
</span><span class="mi">1</span><span class="o">..</span><span class="mi">5</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Foreach-Object</span><span class="w"> </span><span class="nt">-Parallel</span><span class="w"> </span><span class="p">{</span><span class="n">Start-Sleep</span><span class="w"> </span><span class="nt">-s</span><span class="w"> </span><span class="nx">10</span><span class="p">}</span><span class="w"> </span><span class="nt">-TimeoutSeconds</span><span class="w"> </span><span class="mi">1</span><span class="w">
</span></code></pre></div></div>

<p>Here is the output if the time exceed the timeout specified</p>

<p><img src="/images/2020/2020-03-23-powershell7_whatsnew/Terminal_012.png" alt="image-center" class="align-center" /></p>

<h2 id="errorview-conciseview">ErrorView ‘ConciseView’</h2>

<p>A new ErrorView was added to help clarify the message show to the user. Here is a list of the current views available:</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Get Default ErrorView</span><span class="w">
</span><span class="bp">$Error</span><span class="n">View</span><span class="w"> </span><span class="c"># ConciseView is default</span><span class="w">

</span><span class="c"># List ErrorViews available</span><span class="w">
</span><span class="p">[</span><span class="n">Enum</span><span class="p">]::</span><span class="n">getvalues</span><span class="p">([</span><span class="n">System.Management.Automation.ErrorView</span><span class="p">])</span><span class="w">
</span></code></pre></div></div>

<p><img src="/images/2020/2020-03-23-powershell7_whatsnew/Terminal_015.png" alt="image-center" class="align-center" /></p>

<p>We can compare the different ErrorViews available. You will probably be familiar with the <code class="language-plaintext highlighter-rouge">NormalView</code> which was used by default in previous PowerShell versions.</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># CATEGORYVIEW</span><span class="w">
</span><span class="bp">$Error</span><span class="n">View</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">'CategoryView'</span><span class="w">
</span><span class="n">Get-ChildItem</span><span class="w"> </span><span class="nt">-Path</span><span class="w"> </span><span class="nx">/fake</span><span class="w">

</span><span class="c"># NORMALVIEW</span><span class="w">
</span><span class="bp">$Error</span><span class="n">View</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">'NormalView'</span><span class="w">
</span><span class="n">Get-ChildItem</span><span class="w"> </span><span class="nt">-Path</span><span class="w"> </span><span class="nx">/fake</span><span class="w">

</span><span class="c"># CONCISEVIEW</span><span class="w">
</span><span class="bp">$Error</span><span class="n">View</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">'ConciseView'</span><span class="w">
</span><span class="n">Get-ChildItem</span><span class="w"> </span><span class="nt">-Path</span><span class="w"> </span><span class="nx">/fake</span><span class="w">
</span></code></pre></div></div>

<p><img src="/images/2020/2020-03-23-powershell7_whatsnew/Terminal_016.png" alt="image-center" class="align-center" /></p>

<p>Another interesting thing to note in PowerShell 7 is the information showed when a Script generate an error with the <code class="language-plaintext highlighter-rouge">ConciseView</code>.</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">.</span><span class="n">/errortest.ps1</span><span class="w">
</span></code></pre></div></div>

<p><img src="/images/2020/2020-03-23-powershell7_whatsnew/Terminal_017.png" alt="image-center" class="align-center" /></p>

<p>A new property <code class="language-plaintext highlighter-rouge">ErrorAccentColor</code> was also added to the <code class="language-plaintext highlighter-rouge">$host.PrivateData</code> object to control the Error message color (if you need to customize this for your terminal)</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="bp">$Host</span><span class="o">.</span><span class="nf">PrivateData</span><span class="o">.</span><span class="nf">ErrorAccentColor</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">'Magenta'</span><span class="w">
</span></code></pre></div></div>
<p><img src="/images/2020/2020-03-23-powershell7_whatsnew/Terminal_018.png" alt="image-center" class="align-center" /></p>

<h2 id="get-error">Get-Error</h2>

<p>A new Cmdlet <code class="language-plaintext highlighter-rouge">Get-Error</code> was added to retrieve the details of an error. This was previously available but you had to digg inside the Error object to retrieve all these information.</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Detailed view of the fully qualified error (for the last error)</span><span class="w">
</span><span class="n">Get-Error</span><span class="w">

</span><span class="c"># You can also pipe an Error object to the Cmdlet</span><span class="w">
</span><span class="bp">$Error</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Get-Error</span><span class="w">

</span><span class="c"># Retrieve the last 2 errors.</span><span class="w">
</span><span class="n">Get-Error</span><span class="w"> </span><span class="nt">-Newest</span><span class="w"> </span><span class="nx">2</span><span class="w">
</span></code></pre></div></div>

<p><img src="/images/2020/2020-03-23-powershell7_whatsnew/Terminal_019.png" alt="image-center" class="align-center" /></p>

<h2 id="erroractionpreference--break">$ErrorActionPreference = ‘Break’</h2>

<p>A new ErrorActionPreference <code class="language-plaintext highlighter-rouge">Break</code> was added in PowerShell 7. This allows you to enter the debugger when an error occurs or when an exception is raised.</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="bp">$Error</span><span class="n">ActionPreference</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">'Break'</span><span class="w">
</span><span class="n">Get-ChildItem</span><span class="w"> </span><span class="nt">-Path</span><span class="w"> </span><span class="nx">/fake</span><span class="w">
</span></code></pre></div></div>

<p><img src="/images/2020/2020-03-23-powershell7_whatsnew/Terminal_032.png" alt="image-center" class="align-center" /></p>

<h2 id="null-coalescing-operators">Null-Coalescing Operators</h2>

<p>Null coalescing operators removes the need for <code class="language-plaintext highlighter-rouge">if</code> and <code class="language-plaintext highlighter-rouge">else</code> statements if you want to get the value of a statement if it’s not <code class="language-plaintext highlighter-rouge">$null</code> or return something else if it is <code class="language-plaintext highlighter-rouge">$null</code>. Note that this doesn’t replace the check for a boolean value of true or false, it’s only checking if the returned value is <code class="language-plaintext highlighter-rouge">$null</code></p>

<h3 id="operator-">Operator ??</h3>

<p><code class="language-plaintext highlighter-rouge">&lt;statement&gt; ?? &lt;What to do if statement is null&gt;</code></p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">## Before PowerShell 7</span><span class="w">
</span><span class="nv">$x</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="bp">$null</span><span class="w">
</span><span class="kr">if</span><span class="w"> </span><span class="p">(</span><span class="bp">$null</span><span class="w"> </span><span class="o">-eq</span><span class="w"> </span><span class="nv">$x</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="s1">'x is null'</span><span class="p">}</span><span class="w">

</span><span class="c">## PowerShell 7</span><span class="w">
</span><span class="c"># example A - $x is null</span><span class="w">
</span><span class="nv">$x</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="bp">$null</span><span class="w">
</span><span class="nv">$x</span><span class="w"> </span><span class="nf">??</span><span class="w"> </span><span class="s1">'x is null'</span><span class="w">   </span><span class="c"># if $x is null, show 'x is null'. Else show $x value</span><span class="w">

</span><span class="c"># example B - $x is null</span><span class="w">
</span><span class="nv">$x</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="bp">$null</span><span class="w">
</span><span class="nv">$x</span><span class="w"> </span><span class="nf">??</span><span class="w"> </span><span class="nx">2</span><span class="w">

</span><span class="c"># example C - $x is NOT null</span><span class="w">
</span><span class="nv">$x</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">1</span><span class="w">
</span><span class="nv">$x</span><span class="w"> </span><span class="nf">??</span><span class="w"> </span><span class="s1">'x is null'</span><span class="w">

</span><span class="c"># example D - if posh-git is not present, install it</span><span class="w">
</span><span class="p">(</span><span class="n">Get-Module</span><span class="w"> </span><span class="nt">-ListAvailable</span><span class="w"> </span><span class="nx">posh-git</span><span class="p">)</span><span class="w"> </span><span class="nf">??</span><span class="w"> </span><span class="p">(</span><span class="nx">Install-Module</span><span class="w"> </span><span class="nx">posh-git</span><span class="w"> </span><span class="nt">-WhatIf</span><span class="w"> </span><span class="nt">-Force</span><span class="p">)</span><span class="w">

</span><span class="c"># example E - if file 'stuff.txt' exists show content, else get some content and create file</span><span class="w">
</span><span class="p">(</span><span class="n">Get-Content</span><span class="w"> </span><span class="o">.</span><span class="nx">/stuff.txt</span><span class="w"> </span><span class="nt">-ea</span><span class="w"> </span><span class="nx">0</span><span class="p">)</span><span class="w"> </span><span class="nf">??</span><span class="w"> </span><span class="p">((</span><span class="n">iwr</span><span class="w"> </span><span class="s1">'http://lazywinadmin.com'</span><span class="p">)</span><span class="o">.</span><span class="nf">content</span><span class="w"> </span><span class="err">&gt;</span><span class="w"> </span><span class="o">.</span><span class="n">/stuff.txt</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>

<p><img src="/images/2020/2020-03-23-powershell7_whatsnew/Terminal_020.png" alt="image-center" class="align-center" /></p>

<h3 id="operator--1">Operator ??=</h3>

<p><code class="language-plaintext highlighter-rouge">&lt;statement&gt; ??= &lt;Value to assign if statement is null&gt;</code></p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">## Before PowerShell 7</span><span class="w">
</span><span class="nv">$x</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="bp">$null</span><span class="w">
</span><span class="kr">if</span><span class="w"> </span><span class="p">(</span><span class="bp">$null</span><span class="w"> </span><span class="o">-eq</span><span class="w"> </span><span class="nv">$x</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="nv">$x</span><span class="o">=</span><span class="mi">5</span><span class="p">}</span><span class="w">

</span><span class="c">## PowerShell 7</span><span class="w">
</span><span class="nv">$x</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="bp">$null</span><span class="w">
</span><span class="nv">$x</span><span class="w"> </span><span class="nf">??</span><span class="o">=</span><span class="w"> </span><span class="nx">5</span><span class="w">

</span><span class="nv">$x</span><span class="w"> </span><span class="c"># $x eq 5</span><span class="w">
</span></code></pre></div></div>

<p><img src="/images/2020/2020-03-23-powershell7_whatsnew/Terminal_021.png" alt="image-center" class="align-center" /></p>

<h2 id="pipeline-chain-operators">Pipeline Chain Operators</h2>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Summarizing what Pipeline Chain Operators do:</span><span class="w">
</span><span class="n">Invoke-Something</span><span class="w"> </span><span class="o">&amp;&amp;</span><span class="w"> </span><span class="s2">"Execute if Invoke-Something worked"</span><span class="w">
</span><span class="n">Invoke-Something</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="s2">"Execute if Invoke-Something failed"</span><span class="w">
</span></code></pre></div></div>

<p>These two new operators are leveraging <code class="language-plaintext highlighter-rouge">$LASTEXITCODE</code> and <code class="language-plaintext highlighter-rouge">$?</code> variables to know if a statement is successful or not.</p>

<p>As a reminder, here is the behavior of these 2 variables:</p>

<p><img src="/images/2020/2020-03-23-powershell7_whatsnew/Terminal_022.png" alt="image-center" class="align-center" /></p>

<p><img src="/images/2020/2020-03-23-powershell7_whatsnew/Terminal_023.png" alt="image-center" class="align-center" /></p>

<p>Now let’s see how to use the two new operators</p>

<h3 id="operator--2">Operator &amp;&amp;</h3>

<p>The operator <code class="language-plaintext highlighter-rouge">&amp;&amp;</code> (double Ampersand).</p>

<p><code class="language-plaintext highlighter-rouge">&lt;Command A&gt; &amp;&amp; &lt;Command to execute if Command A was successful&gt;</code></p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">## Before PowerShell 7</span><span class="w">
</span><span class="n">Get-Process</span><span class="w"> </span><span class="nt">-id</span><span class="w"> </span><span class="bp">$PID</span><span class="w"> </span><span class="p">;</span><span class="w"> </span><span class="kr">if</span><span class="w"> </span><span class="p">(</span><span class="bp">$?</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="s1">'Second command'</span><span class="w"> </span><span class="p">}</span><span class="w">

</span><span class="c">## PowerShell 7</span><span class="w">
</span><span class="c"># Example A</span><span class="w">
</span><span class="n">Get-Process</span><span class="w"> </span><span class="nt">-id</span><span class="w"> </span><span class="bp">$PID</span><span class="w"> </span><span class="o">&amp;&amp;</span><span class="w"> </span><span class="s1">'Second command'</span><span class="w">

</span><span class="c"># Example B</span><span class="w">
</span><span class="n">install-module</span><span class="w"> </span><span class="nx">adsips</span><span class="w"> </span><span class="nt">-Force</span><span class="w"> </span><span class="o">&amp;&amp;</span><span class="w"> </span><span class="nx">import-module</span><span class="w"> </span><span class="nx">adsips</span><span class="w"> </span><span class="nt">-passthru</span><span class="w">

</span><span class="c"># Example C</span><span class="w">
</span><span class="n">sudo</span><span class="w"> </span><span class="nx">apt</span><span class="w"> </span><span class="nx">update</span><span class="w"> </span><span class="o">&amp;&amp;</span><span class="w"> </span><span class="nx">sudo</span><span class="w"> </span><span class="nx">apt</span><span class="w"> </span><span class="nx">upgrade</span><span class="w">
</span></code></pre></div></div>

<p><img src="/images/2020/2020-03-23-powershell7_whatsnew/Terminal_024.png" alt="image-center" class="align-center" /></p>

<h3 id="operator--3">Operator ||</h3>

<p>The operator <code class="language-plaintext highlighter-rouge">||</code> (double Pipe).</p>

<p><code class="language-plaintext highlighter-rouge">&lt;Command A&gt; &amp;&amp; &lt;Command to execute if Command A failed&gt;</code></p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">## Before PowerShell 7</span><span class="w">
</span><span class="n">Get-Process</span><span class="w"> </span><span class="nt">-id</span><span class="w"> </span><span class="nx">abc</span><span class="w"> </span><span class="p">;</span><span class="w"> </span><span class="kr">if</span><span class="w"> </span><span class="p">(</span><span class="o">-not</span><span class="bp">$?</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">Write-Output</span><span class="w"> </span><span class="s1">'Second command'</span><span class="w"> </span><span class="p">}</span><span class="w">

</span><span class="c">## PowerShell 7</span><span class="w">
</span><span class="c"># Example A</span><span class="w">
</span><span class="c">#  if process 'abc' is not present, write 'Second command'</span><span class="w">
</span><span class="n">Get-Process</span><span class="w"> </span><span class="nt">-id</span><span class="w"> </span><span class="nx">abc</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="n">Write-Output</span><span class="w"> </span><span class="s1">'Second command'</span><span class="w">

</span><span class="c"># Example B</span><span class="w">
</span><span class="c">#  if file does not exist, download content and save to file</span><span class="w">
</span><span class="p">(</span><span class="n">Get-Content</span><span class="w"> </span><span class="nx">~/lazy.txt</span><span class="p">)</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="n">iwr</span><span class="w"> </span><span class="nx">http://lazywinadmin.com</span><span class="w"> </span><span class="nt">-outfile</span><span class="w"> </span><span class="nx">~/lazy.txt</span><span class="w">

</span><span class="c"># Example C (second command does execute because of $ErrorActionPreference 'Continue')</span><span class="w">
</span><span class="c"># ErrorActionPreference obviously is taken into account</span><span class="w">
</span><span class="bp">$Error</span><span class="n">ActionPreference</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">'Continue'</span><span class="w"> </span><span class="c"># Default 'Continue'</span><span class="w">
</span><span class="mi">1</span><span class="n">/0</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="s2">"Wow something went wrong"</span><span class="w">

</span><span class="c"># Example D (second command does not execute because of $ErrorActionPreference 'Stop')</span><span class="w">
</span><span class="bp">$Error</span><span class="n">ActionPreference</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">'Stop'</span><span class="w"> </span><span class="c"># Default 'Continue'</span><span class="w">
</span><span class="mi">1</span><span class="n">/0</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="s2">"Wow something went wrong"</span><span class="w">
</span></code></pre></div></div>

<p><img src="/images/2020/2020-03-23-powershell7_whatsnew/Terminal_026.png" alt="image-center" class="align-center" /></p>

<h2 id="ternary-operator">Ternary Operator</h2>

<p>You can use the ternary operator as a replacement for the <code class="language-plaintext highlighter-rouge">if-else</code> statement in simple conditional cases.</p>

<p><code class="language-plaintext highlighter-rouge">&lt;evaluation&gt; ? &lt;if-true&gt; : &lt;if-false&gt;</code></p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">## Before PowerShell 7</span><span class="w">
</span><span class="kr">if</span><span class="p">((</span><span class="n">Get-Module</span><span class="w"> </span><span class="nt">-Name</span><span class="w"> </span><span class="nx">Adsips</span><span class="w"> </span><span class="nt">-listavailable</span><span class="p">){</span><span class="w">
    </span><span class="s2">"Already installed"</span><span class="w">
</span><span class="p">}</span><span class="kr">else</span><span class="p">{</span><span class="w">
    </span><span class="n">Install-Module</span><span class="w"> </span><span class="nt">-Name</span><span class="w"> </span><span class="nx">ADSIPS</span><span class="w"> </span><span class="nt">-Force</span><span class="w"> </span><span class="nt">-whatif</span><span class="w">
</span><span class="p">}</span><span class="w">

</span><span class="c">## PowerShell 7</span><span class="w">
</span><span class="c"># Example A</span><span class="w">
</span><span class="p">(</span><span class="n">Get-Module</span><span class="w"> </span><span class="nt">-Name</span><span class="w"> </span><span class="nx">Adsips</span><span class="w"> </span><span class="nt">-listavailable</span><span class="p">)</span><span class="w"> </span><span class="nf">?</span><span class="w"> </span><span class="s2">"Already installed"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="p">(</span><span class="n">Install-Module</span><span class="w"> </span><span class="nt">-Name</span><span class="w"> </span><span class="nx">ADSIPS</span><span class="w"> </span><span class="nt">-Force</span><span class="w"> </span><span class="nt">-whatif</span><span class="p">)</span><span class="w">

</span><span class="c"># Example B</span><span class="w">
</span><span class="p">(</span><span class="n">get-service</span><span class="w"> </span><span class="nx">myservice</span><span class="p">)</span><span class="w"> </span><span class="nf">?</span><span class="w"> </span><span class="p">(</span><span class="n">irm</span><span class="w"> </span><span class="nx">http://myservice</span><span class="p">)</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="p">(</span><span class="n">installmyservice.ps1</span><span class="p">;</span><span class="n">irm</span><span class="w"> </span><span class="nx">http://myservice</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>

<p><img src="/images/2020/2020-03-23-powershell7_whatsnew/Terminal_027.png" alt="image-center" class="align-center" /></p>

<h2 id="skip-http-error-with-web-cmdlets">Skip HTTP Error with Web Cmdlets</h2>

<p>The two Web Cmdlets <code class="language-plaintext highlighter-rouge">Invoke-RestMethod</code> and <code class="language-plaintext highlighter-rouge">Invoke-WebRequest</code> received two new parameters: <code class="language-plaintext highlighter-rouge">SkipHttpErrorCheck</code> and <code class="language-plaintext highlighter-rouge">StatusCodeVariable</code>.</p>

<h3 id="skiphttperrorcheck">SkipHttpErrorCheck</h3>

<p>This parameter causes the cmdlets to ignore HTTP error statuses and continue to process responses. The error responses are written to the pipeline just as if they were successful.</p>

<p>In previous versions you would have to parse the error object yourself.</p>

<p>Here is an example WITHOUT it</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Querying a page that does not exist</span><span class="w">
</span><span class="n">Invoke-WebRequest</span><span class="w"> </span><span class="nt">-uri</span><span class="w"> </span><span class="s1">'http://lazywinadmin.com/fakepage'</span><span class="w">
</span></code></pre></div></div>
<p><img src="/images/2020/2020-03-23-powershell7_whatsnew/Terminal_029.png" alt="image-center" class="align-center" /></p>

<p>Here is an example WITH <code class="language-plaintext highlighter-rouge">-SkipHttpErrorCheck</code>. As you can see the response is processed normaly.</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">Invoke-RestMethod</span><span class="w"> </span><span class="nt">-uri</span><span class="w"> </span><span class="s1">'http://lazywinadmin.com/fakepage'</span><span class="w"> </span><span class="nt">-SkipHttpErrorCheck</span><span class="w">
</span></code></pre></div></div>
<p><img src="/images/2020/2020-03-23-powershell7_whatsnew/Terminal_035.png" alt="image-center" class="align-center" /></p>

<h3 id="statuscodevariable">StatusCodeVariable</h3>

<p>This parameter specifies a variable that’s assigned a status code’s integer value.</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$Output</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Invoke-RestMethod</span><span class="w"> </span><span class="nt">-uri</span><span class="w"> </span><span class="s1">'http://lazywinadmin.com/fakepage'</span><span class="w"> </span><span class="nt">-SkipHttpErrorCheck</span><span class="w"> </span><span class="nt">-StatusCodeVariable</span><span class="w"> </span><span class="nx">mystatuscode</span><span class="w">
</span><span class="nv">$mystatuscode</span><span class="w"> </span><span class="c"># Contains the status code</span><span class="w">
</span></code></pre></div></div>
<p><img src="/images/2020/2020-03-23-powershell7_whatsnew/Terminal_036.png" alt="image-center" class="align-center" /></p>

<h2 id="select-string-emphasis">Select-String Emphasis</h2>

<p><code class="language-plaintext highlighter-rouge">Select-String</code> allows you to search accross files for different words, patterns,…</p>

<p>In PowerShell 7, an Emphasis mode was added by default. This can be disabled using <code class="language-plaintext highlighter-rouge">-NoEmphasis</code>.</p>

<h3 id="emphasis-pattern">Emphasis pattern</h3>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># highlights the string that matches the pattern you searched</span><span class="w">
</span><span class="nv">$Files</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"~\code\Presentations\20200310-PowerShell7-whats_new\"</span><span class="w">
</span><span class="n">Get-Childitem</span><span class="w"> </span><span class="s2">"</span><span class="nv">$Files</span><span class="s2">*.txt"</span><span class="w"> </span><span class="o">|</span><span class="w">
    </span><span class="n">Select-String</span><span class="w"> </span><span class="nt">-Pattern</span><span class="w"> </span><span class="s1">'powershell'</span><span class="w">
</span></code></pre></div></div>

<p><img src="/images/2020/2020-03-23-powershell7_whatsnew/Terminal_013.png" alt="image-center" class="align-center" /></p>

<h3 id="emphasis-pattern-with-regex">Emphasis pattern with regex</h3>

<p>Here is another example if you leverage regex, the string matching your pattern will be highlighted</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># highlights the string that matches the pattern you searched</span><span class="w">
</span><span class="nv">$Files</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"~\code\Presentations\20200310-PowerShell7-whats_new\"</span><span class="w">
</span><span class="n">Get-Childitem</span><span class="w"> </span><span class="s2">"</span><span class="nv">$Files</span><span class="s2">*.txt"</span><span class="w"> </span><span class="o">|</span><span class="w">
    </span><span class="n">Select-String</span><span class="w"> </span><span class="nt">-Pattern</span><span class="w"> </span><span class="s1">'github\w+'</span><span class="w">
</span></code></pre></div></div>
<p><img src="/images/2020/2020-03-23-powershell7_whatsnew/Terminal_014.png" alt="image-center" class="align-center" /></p>

<h2 id="split-negative-max-substring">Split Negative max-substring</h2>

<p>When using <code class="language-plaintext highlighter-rouge">-split</code> you can specify the maximum number of times that a string is split. The default is all the substrings split by the delimiter. If there are more substrings, they are concatenated to the final substring. If there are fewer substrings, all the substrings are returned. A value of 0 returns all the substrings.</p>

<p>Introduced in PowerShell 7, you can now specify a negative number. Negative values return the amount of substrings requested starting from the end of the input string.</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$c</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Mercury,Venus,Earth,Mars,Jupiter,Saturn,Uranus,Neptune"</span><span class="w">
</span><span class="nv">$c</span><span class="w"> </span><span class="o">-split</span><span class="w"> </span><span class="s2">","</span><span class="p">,</span><span class="w"> </span><span class="mi">5</span><span class="w">

</span><span class="nv">$c</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Mercury,Venus,Earth,Mars,Jupiter,Saturn,Uranus,Neptune"</span><span class="w">
</span><span class="nv">$c</span><span class="w"> </span><span class="o">-split</span><span class="w"> </span><span class="s2">","</span><span class="p">,</span><span class="w"> </span><span class="nt">-5</span><span class="w">
</span></code></pre></div></div>

<p><img src="/images/2020/2020-03-23-powershell7_whatsnew/Terminal_030.png" alt="image-center" class="align-center" /></p>

<h2 id="new-version-notification">New Version Notification</h2>

<p>By default, once a day PowerShell query online services to determine if a newer version is available. See <a href="https://docs.microsoft.com/en-us/powershell/scripting/whats-new/what-s-new-in-powershell-70?view=powershell-7#new-version-notification">Microsoft documentation</a> for more details.</p>

<p>You can however configure this using the <code class="language-plaintext highlighter-rouge">$Env:POWERSHELL_UPDATECHECK</code> variable. Note that it does not exist by default.</p>

<p>Here is the feature in action by launching PowerShell 7 RC3 installed on my machine.</p>

<p><img src="/images/2020/2020-03-23-powershell7_whatsnew/Terminal_028.png" alt="image-center" class="align-center" /></p>

<h2 id="windows-os-features">Windows OS features</h2>

<p>Some other new features are only available on Windows Operating System. Here is a short summary of what I found.</p>

<h3 id="import-module--usewindowspowershell">Import-Module -UseWindowsPowerShell</h3>

<p>From PowerShell 7, this will open a Windows PowerShell process and load the module specified. This made me think about implicit remoting available when using <code class="language-plaintext highlighter-rouge">-PSSession</code> or <code class="language-plaintext highlighter-rouge">-CimSession</code>.</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">Import-Module</span><span class="w"> </span><span class="nx">ActiveDirectory</span><span class="w"> </span><span class="nt">-UseWindowsPowerShell</span><span class="w">
</span></code></pre></div></div>

<h3 id="graphical-interface-is-back">Graphical interface is back</h3>

<p>The following tools are back in PowerShell 7 (previously only available on Windows PowerShell)</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">Out-GridView</code> example: <code class="language-plaintext highlighter-rouge">Get-Process -Name Chrome | Out-GridView</code></li>
  <li><code class="language-plaintext highlighter-rouge">Get-Help -ShowWindow</code> example: <code class="language-plaintext highlighter-rouge">Get-Help Get-Process -ShowWindow</code></li>
  <li><code class="language-plaintext highlighter-rouge">Show-Command</code> example: <code class="language-plaintext highlighter-rouge">Show-Command -Name Get-Process</code></li>
</ul>

<h3 id="get-hotfix">Get-HotFix</h3>

<p>You can now list the Patches installed inside PowerShell 7.</p>

<h2 id="miscellaneous">Miscellaneous</h2>

<h3 id="group-object--casesensitive-fixed">Group-Object -CaseSensitive fixed</h3>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$myobjects</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">@(</span><span class="w">
    </span><span class="p">[</span><span class="n">PSCustomObject</span><span class="p">]@{</span><span class="w">
        </span><span class="nx">Capitonym</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">'lazy'</span><span class="w">
    </span><span class="p">}</span><span class="w">
    </span><span class="p">[</span><span class="n">PSCustomObject</span><span class="p">]@{</span><span class="w">
        </span><span class="nx">Capitonym</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">'Lazy'</span><span class="w">
    </span><span class="p">}</span><span class="w">
</span><span class="p">)</span><span class="w">
</span><span class="nv">$myobjects</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Group-Object</span><span class="w"> </span><span class="nt">-Property</span><span class="w"> </span><span class="nx">Capitonym</span><span class="w"> </span><span class="nt">-AsHashTable</span><span class="w"> </span><span class="nt">-CaseSensitive</span><span class="w">
</span></code></pre></div></div>

<p><img src="/images/2020/2020-03-23-powershell7_whatsnew/Terminal_034.png" alt="image-center" class="align-center" /></p>

<h3 id="powershell-jobs">PowerShell Jobs</h3>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">## Foreach-Object parallel can use jobs</span><span class="w">
</span><span class="mi">1</span><span class="o">..</span><span class="mi">100</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Foreach-Object</span><span class="w"> </span><span class="nt">-Parallel</span><span class="w"> </span><span class="p">{</span><span class="s2">"Stuff </span><span class="bp">$_</span><span class="s2">"</span><span class="p">}</span><span class="w"> </span><span class="nt">-AsJob</span><span class="w"> </span><span class="o">|</span><span class="n">Receive-Job</span><span class="w"> </span><span class="nt">-Wait</span><span class="w">

</span><span class="c">## Start-job has a WorkDirectory parameter</span><span class="w">
</span><span class="n">Start-job</span><span class="w"> </span><span class="nt">-ScriptBlock</span><span class="w"> </span><span class="p">{</span><span class="s2">"Hey"</span><span class="p">}</span><span class="w"> </span><span class="nt">-WorkingDirectory</span><span class="w"> </span><span class="p">(</span><span class="n">Resolve-Path</span><span class="w"> </span><span class="nx">~</span><span class="p">)</span><span class="o">|</span><span class="n">Receive-Job</span><span class="w"> </span><span class="nt">-Wait</span><span class="w">

</span><span class="c">## Start-job has a PSVersion parameter (only works on Windows)</span><span class="w">
</span><span class="n">Start-job</span><span class="w"> </span><span class="nt">-ScriptBlock</span><span class="w"> </span><span class="p">{</span><span class="s2">"Hey"</span><span class="p">}</span><span class="w"> </span><span class="nt">-PSVersion</span><span class="w"> </span><span class="mf">5.1</span><span class="w"> </span><span class="o">|</span><span class="n">Receive-Job</span><span class="w"> </span><span class="nt">-Wait</span><span class="w">
</span><span class="c">## Start-job - RunAs32 parameter does not work on 64bits systems</span><span class="w">
</span><span class="c">##  to start a 32-bit PowerShell (pwsh) process with RunAs32, you need to have the 32-bit PowerShell installed.</span><span class="w">
</span></code></pre></div></div>

<h3 id="get-history-shows-duration">Get-History shows Duration</h3>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">Get-History</span><span class="w">
</span></code></pre></div></div>

<p><img src="/images/2020/2020-03-23-powershell7_whatsnew/Terminal_033.png" alt="image-center" class="align-center" /></p>

<h2 id="resources">Resources</h2>

<ul>
  <li><a href="https://devblogs.microsoft.com/powershell/announcing-the-powershell-7-0-release-candidate/">Official announcement</a></li>
  <li><a href="https://docs.microsoft.com/en-us/powershell/scripting/whats-new/what-s-new-in-powershell-70?view=powershell-7">Microsoft Documentation, PowerShell 7 What’s new</a></li>
  <li><a href="https://github.com/PowerShell/PowerShell/blob/master/CHANGELOG/7.0.md">ChangeLog</a></li>
  <li><a href="https://docs.microsoft.com/en-us/powershell/scripting/whats-new/what-s-new-in-powershell-70?view=powershell-7#breaking-changes-and-improvements">Breaking changes and Improvements</a></li>
  <li><a href="https://devblogs.microsoft.com/powershell/powershell-foreach-object-parallel-feature/">Foreach-Object -Parallel</a></li>
  <li><a href="https://devblogs.microsoft.com/powershell/secrets-management-module-vault-extensions/">SecretsManagement module</a></li>
  <li><a href="https://docs.microsoft.com/en-us/dotnet/standard/collections/thread-safe/">Using thread safe variable references</a></li>
  <li><a href="https://powershell.anovelidea.org/powershell/ps7now-changes-to-json-cmdlets/">PowerShell 7 - New JSON Cmdlets</a></li>
  <li><a href="https://colincogle.name/blog/2019-12-22-powershell-7-for-programmers.html">PowerShell 7 for Programmers</a></li>
</ul>]]></content><author><name>François-Xavier Cat</name></author><category term="powershell7" /><category term="powershell" /><summary type="html"><![CDATA[Microsoft recently released a new version of PowerShell, this post is a quick run-through the new features and new cmdlets that I presented at the last French PowerShell User Group meeting]]></summary></entry><entry><title type="html">Hacktoberfest PowerShell (french)</title><link href="https://lazywinadmin.github.io/2019/09/hacktoberfest_powershell.html" rel="alternate" type="text/html" title="Hacktoberfest PowerShell (french)" /><published>2019-09-29T00:00:00+00:00</published><updated>2019-09-29T00:00:00+00:00</updated><id>https://lazywinadmin.github.io/2019/09/hacktoberfest_powershell</id><content type="html" xml:base="https://lazywinadmin.github.io/2019/09/hacktoberfest_powershell.html"><![CDATA[<p>Dans le cadre du Hacktoberfest qui ce tient pendant le mois d’Octobre, nous organisons un mini-meetup pour discuter les différentes façons de contribuer a des projets Open Sources sur Github.</p>

<p>Les démos seront évidemment concentrées sur les projets PowerShell de la communauté.</p>

<h2 id="hacktoberfest-">Hacktoberfest ?</h2>

<p><img src="/images/2019/2019-09-29-hacktoberfest_powershell/hacktoberfest2019medium.png" alt="image-center" class="align-center" /></p>

<p>Hacktoberfest - Présenté par DigitalOcean et DEV en partenariat avec GitHub est une célébration d’un mois du logiciel libre. Les maintainers sont invités à guider les contributeurs potentiels vers les questions qui aideront à faire avancer le projet, et les contributeurs ont la possibilité de contribuer aux projets qu’ils aiment et à ceux qu’ils viennent de découvrir. Aucune contribution n’est trop petite - les corrections de bogues et les mises à jour de la documentation sont des moyens valables de participer.</p>

<h2 id="participer-et-gagner-un-t-shirt">Participer et Gagner un t-shirt</h2>

<h3 id="senregistrer">S’enregistrer</h3>

<p>A l’aide de votre compte Github, dirigez-vous vers <a href="https://hacktoberfest.digitalocean.com/">Hacktoberfest</a> et enregistrez-vous.</p>

<h3 id="gagner-un-t-shirt">Gagner un t-shirt</h3>

<p>En plus d’apprendre en contribuant, vous pouvez vous qualifier pour gagner un t-shirt “Hacktoberfest 2019”. Celui ci sera offert aux premier 50 000 personnes ayant soumis au moins 4 pull requests sur des depots public Github (avec ou sans label <code class="language-plaintext highlighter-rouge">Hacktoberfest</code>).</p>

<h3 id="trouver-et-contribuer-a-des-depots-public">Trouver et Contribuer a des depots public</h3>

<p>Pour trouver des issues relier a l’evenement Hacktoberfest, vous pouvez faire une recherche sur Github, exemples:</p>

<ul>
  <li><a href="https://github.com/search?l=Powershell&amp;q=label%3Ahacktoberfest&amp;type=Issues">Issue <code class="language-plaintext highlighter-rouge">Hacktoberfest</code> pour le language <code class="language-plaintext highlighter-rouge">PowerShell</code></a></li>
  <li><a href="https://github.com/search?l=Python&amp;q=label%3Ahacktoberfest&amp;type=Issues">Issue <code class="language-plaintext highlighter-rouge">Hacktoberfest</code> pour le language <code class="language-plaintext highlighter-rouge">Python</code></a></li>
</ul>

<h2 id="besoin-daide--evenement-en-ligne-mardi-1er-octobre-20h-frpsug">Besoin d’aide ? Evenement en ligne Mardi 1er Octobre 20h @frpsug</h2>

<p>Deux options, Rejoindre notre chat et assister a notre meeting en ligne:</p>

<ul>
  <li>Le canal <a href="https://frpsug.com/slack/">#french sur Slack</a></li>
  <li>Le <a href="https://frpsug.com">FrPSUG</a> organise une rencontre en ligne pour discuter du Hacktoberfest et des differentes facons de participer. Details et inscription: <a href="https://www.meetup.com/FrenchPSUG/events/263263437/">Meetup.com</a>
    <ul>
      <li>(Avertissement: Pendant cette rencontre tous les écrans partagés sont enregistrés ainsi que tout ce que disent les participants)</li>
    </ul>
  </li>
</ul>

<h2 id="code-de-conduite">Code de conduite</h2>

<p>Please read our Events Code of Conduct (http://do.co/hacktoberconduct) before attending. Happy hacking!</p>]]></content><author><name>François-Xavier Cat</name></author><category term="hacktoberfest" /><category term="french" /><summary type="html"><![CDATA[Dans le cadre du Hacktoberfest qui ce tient pendant le mois d'Octobre, nous organisons un mini-meetup pour discuter des différentes façons de contribuer a des projets Open Sources sur Github.]]></summary></entry><entry><title type="html">PowerShell - Executing code at specific times</title><link href="https://lazywinadmin.github.io/2019/09/powershell_execute_at_specific_triggertimes.html" rel="alternate" type="text/html" title="PowerShell - Executing code at specific times" /><published>2019-09-06T00:00:00+00:00</published><updated>2019-09-06T00:00:00+00:00</updated><id>https://lazywinadmin.github.io/2019/09/powershell_execute_at_specific_triggertimes</id><content type="html" xml:base="https://lazywinadmin.github.io/2019/09/powershell_execute_at_specific_triggertimes.html"><![CDATA[<p>Tested on:</p>
<ul>
  <li>Windows PowerShell 5.1</li>
  <li>PowerShell 6.2.2</li>
</ul>

<h2 id="quick-tip---how-to-trigger-code-at-specific-times">Quick tip - How to trigger code at specific times</h2>

<p>I recently had someone ask how they could trigger a piece of PowerShell code at different times.</p>

<p>This type of tasks can usually be handled by other systems such as Scheduler, Event or message based systems.</p>

<p>In this particular case, it needed to be handled in the current PowerShell session.</p>

<p>I came up with the following solution.</p>

<p>Let me know in the comment section if you would use another approach and I’ll add you to the post.</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Specify the Execution times</span><span class="w">
</span><span class="nv">$TriggerTimes</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">@(</span><span class="w">
    </span><span class="s1">'4:12:00pm'</span><span class="p">,</span><span class="w">
    </span><span class="s1">'4:12:10pm'</span><span class="p">,</span><span class="w">
    </span><span class="s1">'4:12:20pm'</span><span class="p">,</span><span class="w">
    </span><span class="s1">'2:00:00pm'</span><span class="p">,</span><span class="w">
    </span><span class="s1">'1:00:00pm'</span><span class="w">
</span><span class="p">)</span><span class="w">

</span><span class="c"># Sort in chronologic order</span><span class="w">
</span><span class="c">#  assuming the times format are the same</span><span class="w">
</span><span class="nv">$TriggerTimes</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nv">$TriggerTimes</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Sort-Object</span><span class="w">

</span><span class="kr">foreach</span><span class="w"> </span><span class="p">(</span><span class="nv">$t</span><span class="w"> </span><span class="kr">in</span><span class="w"> </span><span class="nv">$TriggerTimes</span><span class="p">)</span><span class="w">
</span><span class="p">{</span><span class="w">
    </span><span class="c"># Past time ?</span><span class="w">
    </span><span class="kr">if</span><span class="p">((</span><span class="n">Get-Date</span><span class="p">)</span><span class="w"> </span><span class="o">-lt</span><span class="w"> </span><span class="p">(</span><span class="n">Get-Date</span><span class="w"> </span><span class="nt">-Date</span><span class="w"> </span><span class="nv">$t</span><span class="p">))</span><span class="w">
    </span><span class="p">{</span><span class="w">
        </span><span class="c"># Sleeping</span><span class="w">
        </span><span class="kr">while</span><span class="w"> </span><span class="p">((</span><span class="n">Get-Date</span><span class="w"> </span><span class="nt">-Date</span><span class="w"> </span><span class="nv">$t</span><span class="p">)</span><span class="w"> </span><span class="o">-gt</span><span class="w"> </span><span class="p">(</span><span class="n">Get-Date</span><span class="p">))</span><span class="w">
        </span><span class="p">{</span><span class="w">
            </span><span class="c"># Sleep for the remaining time</span><span class="w">
            </span><span class="p">(</span><span class="n">Get-Date</span><span class="w"> </span><span class="nt">-Date</span><span class="w"> </span><span class="nv">$t</span><span class="p">)</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="p">(</span><span class="n">Get-Date</span><span class="p">)</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Start-Sleep</span><span class="w">
        </span><span class="p">}</span><span class="w">

        </span><span class="c"># Trigger event</span><span class="w">
        </span><span class="c">#  insert your code here</span><span class="w">
        </span><span class="s2">"# TriggerTime: '</span><span class="nv">$t</span><span class="s2">' - Executing my code here!"</span><span class="w">

    </span><span class="p">}</span><span class="kr">else</span><span class="p">{</span><span class="s2">"Belong to the past: '</span><span class="nv">$t</span><span class="s2">'"</span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>Output:</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>PS /home/fx/code&gt; ./triggertimes.ps1

Belong to the past: '1:00:00pm'
Belong to the past: '2:00:00pm'
# TriggerTime: '4:12:00pm' - Executing my code here!
# TriggerTime: '4:12:10pm' - Executing my code here!
# TriggerTime: '4:12:20pm' - Executing my code here!
</code></pre></div></div>

<p><img src="/images/2019/2019-09-06-powershell_execute_at_specific_triggertimes/powershell-triggertimes_output.png" alt="image-center" /></p>]]></content><author><name>François-Xavier Cat</name></author><category term="powershell" /><category term="powershell" /><summary type="html"><![CDATA[Someone recently asked me how to trigger code at specific times without leveraging the scheduled tasks]]></summary></entry><entry><title type="html">PowerShell Saturday Paris 2019/10/12 (french)</title><link href="https://lazywinadmin.github.io/2019/09/powershell_saturday_paris.html" rel="alternate" type="text/html" title="PowerShell Saturday Paris 2019/10/12 (french)" /><published>2019-09-06T00:00:00+00:00</published><updated>2019-09-06T00:00:00+00:00</updated><id>https://lazywinadmin.github.io/2019/09/powershell_saturday_paris</id><content type="html" xml:base="https://lazywinadmin.github.io/2019/09/powershell_saturday_paris.html"><![CDATA[<p>Notre événement <strong>PowerShell Saturday Paris</strong> aura lieu dans quelques semaines, le 12 octobre 2019 à partir de 9h00 chez Cellenza.</p>

<p>Cette année l’événement est organisé par Stéphane, Olivier, Laurent et moi-meme. Un enorme merci pour a toute l’equipe pour leurs efforts.</p>

<p>Cependant cette année je ne pourrais pas etre présent sur place pour differentes raisons. Je suis certain que le reste de l’équipe et de la communauté rendront cette événement inoubliable. (Prenez plein de photos ! 😸)</p>

<center>
<img src="https://raw.githubusercontent.com/FrPSUG/media/master/powershellsat/logo/adil/ps-saturday-paris-simple.png" width="40%" />
</center>

<h2 id="powershell-saturday-cest-quoi-">PowerShell Saturday c’est quoi ?</h2>

<ul>
  <li>12 présentations de speakers venant de toute l’Europe (<a href="https://frpsug.com/powershellsat-2019/#agenda">voir l’agenda</a>)</li>
  <li>Des Démo éclairs (lightning demos) auront lieu pendant la pause déjeuner.</li>
  <li>Repas et boissons (Café de bienvenue / déjeuner / gouter)</li>
  <li>T-shirt French PowerShell Saturday (ticket régulier uniquement)</li>
</ul>

<h2 id="inscription">Inscription</h2>

<p><strong>Noter que les inscriptions ont lieu cette année sur <a href="https://pssatparis2019.eventbrite.com">EventBrite</a>.</strong></p>

<p>Les inscriptions sur <a href="https://www.meetup.com/fr-FR/FrenchPSUG/">meetup.com</a> <u>NE seront PAS prise en compte</u>.</p>

<h2 id="agenda-lieu-et-autre-détails">Agenda, Lieu et autre détails</h2>

<p><a href="http://frpsug.com/powershellsat-2019/">Voir la page dédier</a> sur le site du frpsug.com</p>

<h2 id="enregistrements-">Enregistrements ?</h2>

<p>Nous allons tenter d’enregistrer les sessions cette année, mais rien de promis.</p>

<h2 id="merci-aux-sponsors-">Merci aux sponsors !</h2>

<p>Merci a tous les sponsors qui par leur soutien et leur fidélité permettent d’organiser ce type d’événement:</p>

<h3 id="sponsors-platinium">Sponsors Platinium</h3>

<p align="center">
<a href="https://www.scriptrunner.com/en/"><img src="https://frpsug.github.io/images/sponsors/ScriptRunner.png" width="30%" title="ScriptRunner" alt="ScriptRunner" /></a>
</p>

<p align="center">
<a href="https://www.cellenza.com/"><img src="https://frpsug.github.io/images/sponsors/cellenza_.png" width="30%" title="Metsys" alt="Metsys" /></a>
</p>

<p align="center">
<a href="https://www.metsys.fr/"><img src="https://frpsug.github.io/images/sponsors/metsys.png" width="30%" title="Cellenza" alt="Cellenza" /></a>
</p>

<h3 id="sponsors-silver">Sponsors Silver</h3>

<p align="center">
<a href="https://chocolatey.org/"><img src="https://frpsug.github.io/images/sponsors/chocolatey.png" width="30%" title="Chocolatey" alt="Chocolatey" /></a>
</p>

<p align="center">
<a href="https://systemfrontier.com"><img src="https://frpsug.github.io/images/sponsors/System Frontier Logo - Wide.png" width="30%" title="SystemFrontier" alt="SystemFrontier" /></a>
</p>

<h2 id="merci-aux-organisateurs">Merci aux organisateurs</h2>

<p>Un énorme travail de fond a été fait pour organiser cette événement. Merci a tous les organisateurs:</p>

<ul>
  <li>Francois-Xavier Cat (<a href="https://twitter.com/lazywinadmin">@lazywinadmin</a>)</li>
  <li>Stephane Van Gulick (<a href="https://twitter.com/stephanevg">@stephanevg</a>)</li>
  <li>Laurent Lienhard (<a href="https://twitter.com/irontux">@irontux</a>)</li>
  <li>Olivier Miossec (<a href="https://twitter.com/omiossec_med">@omiossec_med</a>)</li>
</ul>

<h2 id="besoin-de-plus-dinformation-">Besoin de plus d’information ?</h2>

<p>Utiliser le <a href="http://frpsug.com/slack/">Channel #french sur Slack.com</a> et <a href="http://frpsug.com/organisateurs/">contacter un des organisateurs du groupe</a>.</p>]]></content><author><name>François-Xavier Cat</name></author><category term="powershell" /><category term="frpsug" /><category term="french" /><summary type="html"><![CDATA[En ce mois de Septembre 2019 nous celebrons le troisieme anniversaire du French PowerShell User group. Pour cette occasion nour organisons a nouveau un evenement a Paris le Samedi 12 Octobre 2019 a Paris]]></summary></entry><entry><title type="html">Microsoft MVP 2019-2020</title><link href="https://lazywinadmin.github.io/2019/07/Microsoft_MVP_2019-2020.html" rel="alternate" type="text/html" title="Microsoft MVP 2019-2020" /><published>2019-07-01T00:00:00+00:00</published><updated>2019-07-01T00:00:00+00:00</updated><id>https://lazywinadmin.github.io/2019/07/Microsoft_MVP_2019-2020</id><content type="html" xml:base="https://lazywinadmin.github.io/2019/07/Microsoft_MVP_2019-2020.html"><![CDATA[<p>I’m very pleased and honored to receive the Microsoft MVP award for another year for the category <b>Cloud and DataCenter Management MVP</b> !</p>

<blockquote>
  <p>Dear Francois-Xavier,<br />
We’re once again pleased to present you with the 2019-2020 Microsoft Most Valuable Professional (MVP) award in recognition of your exceptional technical community leadership. We appreciate your outstanding contributions in the following technical communities during the past year:<br />
   Cloud and Datacenter Management</p>
</blockquote>

<p><strong>I want to thank everyone for their support, the incredible Community and the teams at Microsoft</strong> :bow:</p>

<p>In the last year most my contributions were around:</p>

<ul>
  <li><a href="https://github.com/lazywinadmin">Contributions to Open Source projects</a></li>
  <li><a href="https://frpsug.com/meet">French PowerShell user group</a></li>
</ul>

<p>See my Microsoft MVP profile <a href="https://mvp.microsoft.com/en-us/PublicProfile/5000475" target="_blank">here</a>.</p>

<p><img src="/images/2018/2018-07-01-Microsoft_MVP_2018-2019/MicrosoftMVPLogo.png" alt="image-center" /></p>]]></content><author><name>François-Xavier Cat</name></author><category term="achievement" /><category term="cloud and datacenter management" /><category term="microsoft" /><category term="mvp" /><category term="powershell" /><summary type="html"><![CDATA[It is a great pleasure and honor to receive the Microsoft MVP award for another year]]></summary></entry><entry><title type="html">Updating my blog theme with git</title><link href="https://lazywinadmin.github.io/2019/06/blogtheme_update_with_git.html" rel="alternate" type="text/html" title="Updating my blog theme with git" /><published>2019-06-29T00:00:00+00:00</published><updated>2019-06-29T00:00:00+00:00</updated><id>https://lazywinadmin.github.io/2019/06/blogtheme_update_with_git</id><content type="html" xml:base="https://lazywinadmin.github.io/2019/06/blogtheme_update_with_git.html"><![CDATA[<p>In this short post, I’ll show how I update my static website hosted on github using git.</p>

<p>I moved my blog from Google Blogger to Github Pages and Jekyll about 2 years ago.</p>

<ol>
  <li>I found a Jekyll theme that I liked,</li>
  <li>forked the repository,</li>
  <li>rename the repository to <code class="language-plaintext highlighter-rouge">&lt;github username&gt;.github.io</code></li>
  <li>Blog up and running! 🚀</li>
  <li>You can now clone your fork locally and start writting blog posts.</li>
</ol>

<p>The migration of my old blog posts and comment was another story 😶.</p>

<h2 id="starting-a-blog-from-an-existing-theme">Starting a blog from an existing theme</h2>

<p>We can summarize the workflow described above like this:</p>

<ol>
  <li>Fork a repository (blog theme source)</li>
  <li>Clone your fork locally</li>
  <li>Create custom content locally and commit back to your fork</li>
  <li>Static website gets regenerated each time you commit to your fork</li>
</ol>

<p><img src="/images/2019/2019-06-29-blogtheme_update_with_git/step1.png" alt="image-center" class="align-center" /></p>

<h2 id="blog-theme-update">Blog theme update</h2>

<p>However, when the Blog theme author release a new version, <strong>how to you pull the latest changes ?</strong></p>

<p><img src="/images/2019/2019-06-29-blogtheme_update_with_git/step2.png" alt="image-center" class="align-center" /></p>

<p>One of the great advantage of having my blog content hosted on git is that I can easily use git to pull the last updates from the jekyll theme used.</p>

<p>In my case I’m using the <a href="https://github.com/mmistakes/minimal-mistakes">Minimal Mistakes</a> Jekyll theme from <a href="https://github.com/mmistakes">mmistakes</a>. You’ll need to adapt the following code for the theme that you are using.</p>

<ol>
  <li><strong>List your remote repositories</strong>: You must first ensure there’s an upstream remote. If you forked the theme’s repo then you’re likely good to go.
    <ul>
      <li>To double check, run <code class="language-plaintext highlighter-rouge">git remote -v</code> and verify that you can fetch from origin <code class="language-plaintext highlighter-rouge">https://github.com/mmistakes/minimal-mistakes.git</code></li>
    </ul>
  </li>
  <li><strong>Add your remote repositories (optional)</strong>: To add it manually you can do the following: <code class="language-plaintext highlighter-rouge">git remote add upstream https://github.com/mmistakes/minimal-mistakes.git</code></li>
  <li><strong>Pull the changes</strong>: <code class="language-plaintext highlighter-rouge">git pull upstream master</code>
    <ul>
      <li>Depending on the amount of customizations you’ve made after forking, there’s likely to be merge conflicts. Work through any conflicting files Git flags, staging the changes you wish to keep, and then commit them. You can also do these steps using Microsoft Visual Studio Code (see below).</li>
    </ul>
  </li>
  <li><strong>Push the change to your fork</strong>: <code class="language-plaintext highlighter-rouge">git push</code></li>
</ol>

<h2 id="conflicts">Conflicts</h2>

<p>When pulling the changes from the Upstream (as see above) you might see some conflicts, example during my last upgrade:</p>

<p><a href="/images/2019/2019-06-29-blogtheme_update_with_git/git_conflicts.png"><img src="/images/2019/2019-06-29-blogtheme_update_with_git/git_conflicts.png" alt="image-center" class="align-center" /></a></p>

<p>You resolved these issues using Visual Studio, here is an example:</p>

<p><img src="/images/2019/2019-06-29-blogtheme_update_with_git/git_conflicts-vscode.png" alt="image-center" class="align-center" /></p>]]></content><author><name>François-Xavier Cat</name></author><category term="github" /><category term="git" /><category term="jekyll" /><summary type="html"><![CDATA[My blog is a static site hosted with Github Pages. I initially forked a theme that I liked and now I need to fetch the latest updates...]]></summary></entry></feed>