<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>An IT Blog</title>
    <link>https://anitblog.no/</link>
    <description></description>
    <pubDate>Fri, 15 May 2026 13:50:07 +0200</pubDate>
    <item>
      <title>New blog</title>
      <link>https://anitblog.no/new-blog</link>
      <description>&lt;![CDATA[I decided to move myself off Wordpress to something simpler since this blog has been laying inactive for a long while.&#xA;&#xA;New blog is running on Writefreely. Which is a nice project for simple blogging.&#xA;All previous content has been copied over with a quick conversion to fit the new format.  &#xA;No content has been edited beyond that.]]&gt;</description>
      <content:encoded><![CDATA[<p>I decided to move myself off Wordpress to something simpler since this blog has been laying inactive for a long while.</p>

<p>New blog is running on <a href="https://writefreely.org">Writefreely</a>. Which is a nice project for simple blogging.
All previous content has been copied over with a quick conversion to fit the new format.<br>
No content has been edited beyond that.</p>
]]></content:encoded>
      <guid>https://anitblog.no/new-blog</guid>
      <pubDate>Sat, 07 Sep 2024 16:20:28 +0000</pubDate>
    </item>
    <item>
      <title>Kerberos - a guide</title>
      <link>https://anitblog.no/kerberos-a-guide</link>
      <description>&lt;![CDATA[  Originally posted on 2019-10-12 on the old blog&#xA;&#xA;There are many guides out there on how to implement Kerberos for application x-y-x.&#xA;&#xA;But there are surprisingly few going into some of details that are important for the understanding what you are doing exactly.&#xA;&#xA;So with this article I am going to hopefully help you understand how Kerberos works in a general sense, and that is not as difficult to implement as you might think (as long as you have the all the pieces of the puzzle).&#xA;&#xA;!--more--&#xA;&#xA;What is Kerberos?&#xA;&#xA;Kerberos is a modern authentication protocol for strong and secure authentication in a network and indeed across any network.  &#xA;It is safe enough to be used across the the internet, but is rarely used in a configuration like that because the setup requirements makes it impractical.&#xA;&#xA;Using Kerberos requires pre-existing knowledge of you client endpoints which is simply not practical in a public facing site.  &#xA;But it can technically be used with a internet roaming (pre-configured) client, as long as the client can find and talk to the KDC and app server.&#xA;&#xA;Kerberos it is in active developed by Massachusetts Institute of Technology.  &#xA;More information can be found at its official site at MIT here.&#xA;&#xA;How does Kerberos work?&#xA;&#xA;The exact workings of Kerberos can be rather complicated.  &#xA;But in general authentication to a service using Kerberos is between 3 devices; the client, the Key distribution center (KDC) and the application server.&#xA;&#xA;Here is the (very simplified) process to access a resource on the app server using Kerberos:&#xA;&#xA;Client sends a authentication request (ASREQ).&#xA;KDC replies with to the (successful) request with a encrypted access ticket &#34;TGT&#34; (ASREP).&#xA;Client requests a service ticket for the app server from the KDC using the TGT ticket (TGSREQ).&#xA;KDC replies with a service ticket encrypted with the app server&#39;s secret key (TGSREP).&#xA;Client requests access to a resource on the app server using the service ticket (app server decrypts it using its own key) (APREQ).&#xA;Optional: App Server replies to client to prove it the server the client is expecting (when mutual authentication is required) (APREP).&#xA;&#xA;Going into the details would be a article all on its own, but the Kerberos Consortium has an excellent tutorial available here for those that are curious.&#xA;&#xA;Kerberos in practice&#xA;&#xA;Armed with the knowledge above we can go into the practical workings of Kerberos. And while I am going to focus on the implementation in a Microsoft Active Directory environment, I will start by killing off some myths.&#xA;&#xA;For simplicity sake i will refer to Microsoft Active Directory as &#34;AD DS&#34;.&#xA;&#xA;Myth: Kerberos authentication can only be used by clients in a a AD DS enviroment&#xA;&#xA;This is perhaps one of the most important misconceptions to get rid of.  &#xA;As you might have noticed above Kerberos is a protocol under the MIT licence, and as such it has been implemented in various ways (AD DS, MIT Kerberos, Heimdal and so on).  &#xA;Per krb5-1.13 onwards; interoperability between the implementations should be quite good, since support for a lot of the extensions has been added to the original source (ref.: Kerberos Features).&#xA;&#xA;The only real requirements for using Kerberos from the client side is that it has a Kerberos client installed, and that it can communicate with the KDC to get it&#39;s tickets.  &#xA;Windows and MacOS comes with a Kerberos client pre-installed, and most Linux distros has a pre-compiled version available in their repositories.  &#xA;But basically it can work on any OS as long as you can compile the source for it.&#xA;&#xA;There is of course also the requirements that the application or service can negotiate the protocol properly as well.  &#xA;For example the Chromium project properly supports negotiating the protocol in HTTP scenarios.&#xA;&#xA;Fun fact: You can add your Windows workgroup computer to a Kerberos realm using the ksetup tool. Though if it is a AD DS realm I recommend joining the computer properly to the domain.&#xA;&#xA;Myth: Kerberos is an outdated authentication protocol&#xA;&#xA;This is one i heard the other day.  &#xA;When I asked someone to look into implementing Kerberos on a MS Exchange server (to move away from NTLM), I was told that this was unnecessary work:  &#xA;&#34;Kerberos is an outdated method that will be replaced by a more modern authentication method when moving to Office365.&#34;&#xA;&#xA;Granted Kerberos is not entirely suited for authentication for end user services using Azure AD across the internet, but it is in no way or form an outdated protocol.  &#xA;Inside a on-premise AD DS it is by far the most seamless and secure SSO solution you can implement (maybe with the exception of using certificates, but the added complexity there can be immense).&#xA;&#xA;Concepts for successful use of Kerberos&#xA;&#xA;There are three primary concepts to understand in a Kerberos setup; realm, principal and ticket.&#xA;&#xA;Realm&#xA;&#xA;A realm indicates the boundaries in which the authentication is handled.  &#xA;In AD DS this is for all intents and purposes basically what we are talking about when we talk about a domain.  &#xA;In authentication scenarios this is often written as for example @ANITBLOG.NO.&#xA;&#xA;Note: In Kerberos the realm name is case sensitive, in most situations it is common to simply write it in upper case.&#xA;&#xA;Ticket&#xA;&#xA;There are a lot of parts to a ticket, but for practical purposes a ticket is what identifies principals.&#xA;&#xA;The tickets are encrypted with with the secrets (passwords) of the various principals.  &#xA;A ticket is reusable and it is not possible for a KDC to control its use after it has been transmitted (you can only really deny the creation of new ones).  &#xA;Therefore a ticket has several properties indicating it&#39;s use, and a predefined lifetime to limit potential abuse.&#xA;&#xA;By default the lifetime of a ticket is 10 hours but this can be changed (usually shortened) depending on requirements.&#xA;&#xA;Principals&#xA;&#xA;Principals are the identifier for entities in a authentication database.  &#xA;In short they are what identifies a user, host or service.  &#xA;This is probably the part that trips people up the most during configuring Kerberos.&#xA;&#xA;First a thing to note: In a AD DS you have probably touched upon the property &#34;userPrincipalName&#34; (UPN).&#xA;This is not the same as a Kerberos principal.&#xA;UPN are configured as an alternative logon username and is us usually configured to the user&#39;s mail address (description of the property can be found at MS Docs).&#xA;It has no bearing on the Kerberos authentication process.&#xA;&#xA;USER principal&#xA;&#xA;The user principal name identifier of an AD object is based on it&#39;s sAMAccountName.&#xA;&#xA;For for a user with the sAMAccountName &#34;johnsmith&#34;, the full principal name would then be &#34;johnsmith@ANITBLOG.NO&#34;.&#xA;&#xA;For computer objects in AD DS, they get a &#34;$&#34; added to the sAMAccountName automatically.  &#xA;So in that case the user principal name for the computer account would be &#34;mycomputer$@ANITBLOG.NO&#34;.&#xA;&#xA;The user principal name is what would be used during the user identification part of the authentication process (ASREQ/ASREP).&#xA;&#xA;Service Principal&#xA;&#xA;The &#34;service princpal name&#34; (SPN) is what identfies a service in the Kerberos authentication process (TGSREQ/TGSREP).&#xA;&#xA;In AD DS this is defined in the AD-object attribute servicePrincipalName.  &#xA;This is a multi string attribute. And can contain several identifiers.&#xA;&#xA;When the KDC gets a request for a service ticket it will look for a SPN matching the request.  &#xA;If it finds a match it will send back a service ticket encrypted with the credentials of the AD-object the that has the SPN assigned.  &#xA;If the KDC is unable to find a SPN matching the request, then the authentication fails.&#xA;&#xA;The above is extremely important to note and is why Kerberos often fails!  &#xA;For Kerberos to work the correct SPN must be found!&#xA;&#xA;Service principals are usually written as service/host so for a http (or https) server in my example realm the SPN would be something like http/webserver.anitblog.no.  &#xA;The full service principal name would then be: http/webserver.anitblog.no@ANITBLOG.NO.&#xA;&#xA;Also remember that this is reliant on name resolution, so if you access the server with only the NetBIOS name you would also need a entry for http/webserver too!&#xA;&#xA;Herein also lies another stumbling block; the DNS name resolution.  &#xA;If you use a A/AAAA record to reach your server all is well, the SPN gets resolved correctly.  &#xA;However if you use a CNAME then you might get issues.&#xA;&#xA;If in our case webserver.anitblog.no is a alias for iishost.anitblog.no, then the SPN that will be requested will most likely be for http/iishost.anitblog.no instead, which might fail depending on configured SPN!&#xA;&#xA;Usually the simplest is to avoid CNAME records and use A/AAAA records instead.  &#xA;Depending on the use case you might simply add the SPN needed to the host server.&#xA;&#xA;Or in case of load balancing; you would use a service account with the correct SPN running the service itself, and have the DNS record resolve to the load balancer.&#xA;&#xA;The balancer then redirects the traffic to the back-end servers which can decrypt the service tickets using the service account credentials.  &#xA;Usually no authentication should be handled on the balancer itself, but there might be scenarios where this is needed.  &#xA;But that would be out of scope for this guide.&#xA;&#xA;The &#34;HOST&#34; SPN&#xA;&#xA;Note that there is a special type of service called HOST. This is actually a alias for several known service types.&#xA;&#xA;The alias is defined in the sPNMappings attribute on the Directory Service object found at:&#xA;&#xA;&#34;CN=Directory Service,CN=Windows NT,CN=Services,CN=Configuration,DC=EXAMPLE,DC=COM&#34;. &#xA;&#xA;In general unless you have a very good reason you should avoid messing with this attribute.  &#xA;But it is useful to be aware of this.&#xA;&#xA;Also if a specific entry is made for one of the services HOST is an alias for, then the specific entry will be used and the HOST alias is ignored.&#xA;&#xA;The SPN search pattern is basically something like this (using http/ as an example):&#xA;KDC looks for &#34;http/host&#34; -  &#34;http/host&#34; found on object -  All good!&#xA;&#xA;KDC looks for &#34;http/host&#34; -  &#34;http/host&#34; not found -  substituting &#34;http/host&#34; with &#34;HOST/host&#34; -  &#34;HOST/host&#34; found -  All Good!&#xA;&#xA;KDC looks for &#34;http/host&#34; -  &#34;http/host&#34; not found -  substituting &#34;http/host&#34; with &#34;HOST/host&#34; -  &#34;HOST/host&#34; not found -  Fail!  &#xA;&#xA;Working with service principal names&#xA;&#xA;So now we know a bit on the authentication process for Kerberos.  &#xA;Lets take a closer look at SPN that are required for all this to work.&#xA;&#xA;Modifying SPN&#xA;&#xA;As noted earlier the SPN are defined in the AD attribute servicePrincipalName this means that any method of editing AD attributes of a object is valid for for modifying SPN.&#xA;&#xA;The most commonly used way is to use the tool setspn.exe but it is by no means the only method (here is a reference for the tool).&#xA;&#xA;You can also use the Active Directory PowerShell cmdlets, for example by using parameter -ServicePrincipalName.  &#xA;Or you can modify it using the PowerShell accelerator [ADSI].&#xA;&#xA;You can of course also edit it using GUI tools like &#34;AD Users and Computers&#34;, &#34;ADSI Edit&#34; or even ldp.exe.&#xA;&#xA;Using setspn.exe is pretty straight forward like this:&#xA;&#xA;This will assign the SPN &#34;http/webhost.anitblog.no&#34; to the AD object  &#34;iishost&#34; &#xA;&#xA;setspn.exe -s http/webhost.anitblog.no iishost&#xA;&#xA;Adding SPN using the PowerShell cmdlets:&#xA;&#xA;This will assign the SPN &#34;http/webhost.anitblog.no&#34; to the AD computer &#34;iishost&#34;&#xA;&#xA;Set-ADComputer -Identity &#34;iishost$&#34; -ServicePrincipalNames (@{Add=&#34;http/webhost.anitblog.no&#34;})&#xA;&#xA;Note that we are using the sAMAccountName here so we add a $ at the end of the identity.&#xA;&#xA;While only showing for the cmdlet ADComputer, process is similar on ADUserand ADServiceAccount.&#xA;&#xA;ADObject cmdlets require a bit more effort to use but they could allow you to make a script that works regardless of AD object type.&#xA;&#xA;Here we first need to find the object using a Get-ADObject filter.&#xA;Then we pipe it to Set-ADObject using the parameter -Add to add our SPN to the servicePrincipalName attribute.&#xA;&#xA;Get-ADObject -Filter &#34;sAMAccountName -eq &#39;iishost$&#39;&#34; | Set-ADObject -Add @{servicePrincipalName=&#34;http/webhost.anitblog.no&#34;}&#xA;&#xA;Using ADSI can be a bit difficult but nonetheless possible as well:&#xA;&#xA;Create the ADSI object&#xA;&#xA;[ADSI]$ADObject = (([adsisearcher]&#39;sAMAccountName=iishost$&#39;).FindOne()).Path&#xA;&#xA;Append SPN to the object&#xA;&#xA;$ADObject.PutEx(&#39;3&#39;, &#39;servicePrincipalName&#39;, @(&#39;http/webhost.anitblog.no&#39;))&#xA;&#xA;Write info back to directory&#xA;&#xA;$ADObject.SetInfo()&#xA;&#xA;Working with ADSI is pretty low-level as you are working directly with .net classes.  &#xA;You rarely will need to do this, but it should always be available on any Windows computer past Windows 7.&#xA;&#xA;Some more details on the PutEx method can be found in MS Docs.  &#xA;Also here is a quick reference the PutEx actions:  &#xA;CLEAR = 1   UPDATE = 2   APPEND = 3   DELETE = 4&#xA;&#xA;In general most methods will fail if you try to create duplicate SPN.  &#xA;But some tools might misbehave so it can still happen....&#xA;&#xA;Finding SPN&#xA;&#xA;In any case it is useful to know some commands to check if an SPN has been set, so here are a few ways to do that.  &#xA;If you are trying just to quickly check if a SPN is defined use you can use:&#xA;&#xA;setspn.exe -Q SPN&#xA;&#xA;Using PowerShell cmdlet Get-ADObject you can do it like this (this also support wildcards):&#xA;&#xA;Get-ADObject -Filter &#39;servicePrincipalName -eq &#34;http/webserver.anitblog.no&#34;&#39; -Properties Name,servicePrincipalName&#xA;&#xA;And using [ADSISearcher]:&#xA;&#xA;([adsisearcher]&#39;servicePrincipalName=http/webserver.anitblog.no&#39;).FindAll()&#xA;&#xA;If you are simply trying to find any duplicates while troubleshooting the most simple way is:&#xA;&#xA;setspn.exe -X&#xA;&#xA;If you are automating I would strongly recommend using either the PowerShell cmdlets or if the AD-cmdlets are not installed, use the accelerators (which should always be available from Win 7 and onwards).&#xA;These will make it easier to handle errors since they will output native .net objects or exit codes you can use.&#xA;If you were to use setspn.exe you would have to parse the string output.&#xA;&#xA;Putting it all into use&#xA;&#xA;Now we should have a decent grasp of the requirements on the KDC side of things so lets move on practicals.  &#xA;I am not going to dive very deep into this, as that would warrant it&#39;s own article. But we will go over the basics.&#xA;&#xA;Clients&#xA;&#xA;On the client side; if your Windows client is in the domain, no further configuration is needed to use Kerberos.  &#xA;All of this is handled in the background.&#xA;&#xA;On MacOS or Linux (or Windows client in a workgroup) you will have to configure you Kerberos client to be able to get valid tickets from the Kerberos realm.  &#xA;In most cases this involves invoking kinit, or using ksetup on Windows.  &#xA;Of course it is possible to use other Kerberos implementations on Windows depending on requirements as well.&#xA;&#xA;Also to note: Microsoft has expanded Kerberos with something called &#34;Service4User&#34; (S4U).&#xA;This is also often referred to as protocol transition.&#xA;This extension allows a service to contact a KDC on behalf of a client if they can provide the correct credentials through other means.&#xA;This can be by for example; username and password (basic, NTLM) or client certificate.&#xA;Details on the extension can be found here.&#xA;&#xA;Servers&#xA;&#xA;Servers have pretty much the same requirements as clients.  &#xA;They need to have a working Kerberos client, so if your Windows Servers are joined to the domain, you are all good.&#xA;&#xA;For other platforms you would usually need to create Kerberos keytabs to store their credentials, and use those with for example kinit.  &#xA;In AD DS, keytab creation is done with ktpass, for other platforms this is usually done with the tool ktadd.&#xA;&#xA;VERY IMPORTANT!It cannot be stressed enough but a keytab is very much the equivalent of storing a username and password in a clear text file.And as such it must be protected in the same way (absolutely do not send it by e-mail for example).&#xA;&#xA;For your services; as long as you assign the correct SPN to the account running your service (computer, user or service account).  &#xA;Or the server running the service can access the credentials on behalf of the account with the SPN, things should just work™ (mostly depending on the service itself)....&#xA;&#xA;Summary&#xA;&#xA;So to summary this very long article; while Kerberos itself is a complex beast, using it needs not be so.&#xA;&#xA;In your AD DS all the pieces are already in place, so the most important detail is simply to set the SPN correctly.&#xA;&#xA;Second important part is to that pay attention to how the service names gets resolved by the DNS.  &#xA;This is a vital part of how the client figures out just which service they are requesting access to (so be careful when using CNAME).&#xA;&#xA;But before we leave I have a few points that needs to be covered.&#xA;&#xA;IIS Negotiate&#xA;&#xA;On a IIS server with Windows Authentication it is strongly recommended to only have Negotiate as the provider, unless you have a specific specific issue.  &#xA;The HTTP Auth Negotiate method is implemented in most http clients (like in Chromium as mentioned earlier), and it will negotiate the use of strongest available authentication method.&#xA;&#xA;Usually this means that Kerberos will first be attempted, and if that fails it will fall back to NTLM.&#xA;&#xA;You can also force the use of Kerberos by using Negotiate:Kerberos if the service should only be available when authenticated with Kerberos.&#xA;&#xA;Kerberos encryption ciphers&#xA;&#xA;In short Kerberos can work using several encryption types.  &#xA;For a AD DS these are:  &#xA;DESCBCCRC  &#xA;DESCBCMD5  &#xA;RC4HMACMD5  &#xA;AES128HMACSHA1  &#xA;AES256HMACSHA1&#xA;&#xA;Of these it is only recommended to use the AES ones.  &#xA;The DES type are disabled by default in most versions of AD DS, but RC4 is still enabled by default.  &#xA;It is strongly recommended that the RC4 cipher is disabled and that only AES types are allowed.  &#xA;Here is Microsoft&#39;s information on this.&#xA;&#xA;When you are working on disabling RC4 pay close attention if the domain is part of a forest.  &#xA;In that case the domain trust must be marked as supporting AES as well.  &#xA;Microsoft has a KB on this here.&#xA;&#xA;Comments&#xA;&#xA;While I have have only glanced by some points, I hope this will be a helpful primer on understanding and fully utilizing Kerberos.&#xA;&#xA;As always; please comment if there is something that incorrect or unclear. I&#39;ll be happy to correct it.&#xA;]]&gt;</description>
      <content:encoded><![CDATA[<blockquote><p>Originally posted on 2019-10-12 on the old blog</p></blockquote>

<p>There are many guides out there on how to implement Kerberos for application x-y-x.</p>

<p>But there are surprisingly few going into some of details that are important for the understanding what you are doing exactly.</p>

<p>So with this article I am going to hopefully help you understand how Kerberos works in a general sense, and that is not as difficult to implement as you might think (as long as you have the all the pieces of the puzzle).</p>



<h2 id="what-is-kerberos" id="what-is-kerberos">What is Kerberos?</h2>

<p>Kerberos is a modern authentication protocol for strong and secure authentication in a network and indeed across <em>any</em> network.<br>
It is safe enough to be used across the the internet, but is rarely used in a configuration like that because the setup requirements makes it impractical.</p>

<p>Using Kerberos requires pre-existing knowledge of you client endpoints which is simply not practical in a public facing site.<br>
But it can technically be used with a internet roaming (pre-configured) client, as long as the client can find and talk to the KDC and app server.</p>

<p>Kerberos it is in active developed by Massachusetts Institute of Technology.<br>
More information can be found at its official site at MIT <a href="https://web.mit.edu/Kerberos/">here</a>.</p>

<h2 id="how-does-kerberos-work" id="how-does-kerberos-work">How does Kerberos work?</h2>

<p>The exact workings of Kerberos can be rather complicated.<br>
But in general authentication to a service using Kerberos is between 3 devices; the client, the Key distribution center (KDC) and the application server.</p>

<p>Here is the (very simplified) process to access a resource on the app server using Kerberos:</p>

<pre><code>* Client sends a authentication request (AS_REQ).
* KDC replies with to the (successful) request with a encrypted access ticket &#34;TGT&#34; (AS_REP).
* Client requests a service ticket for the app server from the KDC using the TGT ticket (TGS_REQ).
* KDC replies with a service ticket encrypted with the app server&#39;s secret key (TGS_REP).
* Client requests access to a resource on the app server using the service ticket (app server decrypts it using its own key) (AP_REQ).
* Optional: App Server replies to client to prove it the server the client is expecting (when mutual authentication is required) (AP_REP).
</code></pre>

<p>Going into the details would be a article all on its own, but the Kerberos Consortium has an excellent tutorial available <a href="https://kerberos.org/software/tutorial.html">here</a> for those that are curious.</p>

<h2 id="kerberos-in-practice" id="kerberos-in-practice">Kerberos in practice</h2>

<p>Armed with the knowledge above we can go into the practical workings of Kerberos. And while I am going to focus on the implementation in a Microsoft Active Directory environment, I will start by killing off some myths.</p>

<pre><code>For simplicity sake i will refer to Microsoft Active Directory as &#34;AD DS&#34;.
</code></pre>

<h4 id="myth-kerberos-authentication-can-only-be-used-by-clients-in-a-a-ad-ds-enviroment" id="myth-kerberos-authentication-can-only-be-used-by-clients-in-a-a-ad-ds-enviroment">Myth: <em>Kerberos authentication can only be used by clients in a a AD DS</em> enviroment</h4>

<p>This is perhaps one of the most important misconceptions to get rid of.<br>
As you might have noticed above Kerberos is a protocol under the MIT licence, and as such it has been implemented in various ways (AD DS, MIT Kerberos, Heimdal and so on).<br>
Per <em><code>krb5-1.13</code></em> onwards; interoperability between the implementations should be quite good, since support for a lot of the extensions has been added to the original source (ref.: <a href="https://web.mit.edu/Kerberos/krb5-latest/doc/mitK5features.html"><em>Kerberos Features</em></a>).</p>

<p>The only real requirements for using Kerberos from the client side is that it has a Kerberos client installed, and that it can communicate with the KDC to get it&#39;s tickets.<br>
Windows and MacOS comes with a Kerberos client pre-installed, and most Linux distros has a pre-compiled version available in their repositories.<br>
But basically it can work on any OS as long as you can compile the source for it.</p>

<p>There is of course also the requirements that the application or service can negotiate the protocol properly as well.<br>
<a href="https://www.chromium.org/developers/design-documents/http-authentication">For example the Chromium project properly supports negotiating the protocol in HTTP scenarios</a>.</p>

<pre><code>Fun fact: You can add your Windows workgroup computer to a Kerberos realm using the ksetup tool. Though if it is a AD DS realm I recommend joining the computer properly to the domain.
</code></pre>

<h4 id="myth-kerberos-is-an-outdated-authentication-protocol" id="myth-kerberos-is-an-outdated-authentication-protocol">Myth: <em>Kerberos is an outdated authentication protocol</em></h4>

<p>This is one i heard the other day.<br>
When I asked someone to look into implementing Kerberos on a MS Exchange server (to move away from NTLM), I was told that this was unnecessary work:<br>
<em>“Kerberos is an outdated method that will be replaced by a more modern authentication method when moving to Office365.”</em></p>

<p>Granted Kerberos is not entirely suited for authentication for end user services using Azure AD across the internet, but it is in no way or form an outdated protocol.<br>
Inside a on-premise AD DS it is by far the most seamless and secure SSO solution you can implement (maybe with the exception of using certificates, but the added complexity there can be immense).</p>

<h3 id="concepts-for-successful-use-of-kerberos" id="concepts-for-successful-use-of-kerberos">Concepts for successful use of Kerberos</h3>

<p>There are three primary concepts to understand in a Kerberos setup; <em>realm</em>, <em>principal</em> and <em>ticket</em>.</p>

<h4 id="realm" id="realm">Realm</h4>

<p>A realm indicates the boundaries in which the authentication is handled.<br>
In AD DS this is for all intents and purposes basically what we are talking about when we talk about a domain.<br>
In authentication scenarios this is often written as for example <em><code>@ANITBLOG.NO</code></em>.</p>

<p><em><code>Note: In Kerberos the realm name is case sensitive, in most situations it is common to simply write it in upper case.</code></em></p>

<h4 id="ticket" id="ticket">Ticket</h4>

<p>There are a lot of parts to a ticket, but for practical purposes a ticket is what identifies principals.</p>

<p>The tickets are encrypted with with the secrets (passwords) of the various principals.<br>
A ticket is reusable and it is not possible for a KDC to control its use after it has been transmitted (you can only really deny the creation of new ones).<br>
Therefore a ticket has several properties indicating it&#39;s use, and a predefined lifetime to limit potential abuse.</p>

<pre><code>By default the lifetime of a ticket is 10 hours but this can be changed (usually shortened) depending on requirements.
</code></pre>

<h4 id="principals" id="principals">Principals</h4>

<p>Principals are the identifier for entities in a authentication database.<br>
In short they are what identifies a user, host or service.<br>
This is probably the part that trips people up the most during configuring Kerberos.</p>

<pre><code>First a thing to note: In a AD DS you have probably touched upon the property &#34;userPrincipalName&#34; (UPN).
This is not the same as a Kerberos principal.
UPN are configured as an alternative logon username and is us usually configured to the user&#39;s mail address (description of the property can be found at MS Docs).
It has no bearing on the Kerberos authentication process.
</code></pre>

<h5 id="user-principal" id="user-principal">USER principal</h5>

<p>The user principal name identifier of an AD object is based on it&#39;s <code>sAMAccountName</code>.</p>

<p>For for a user with the <code>sAMAccountName</code> “<code>johnsmith</code>”, the full principal name would then be “<em><code>johnsmith@ANITBLOG.NO</code></em>”.</p>

<p>For computer objects in AD DS, they get a “<code>$</code>” added to the <code>sAMAccountName</code> automatically.<br>
So in that case the user principal name for the computer account would be “<em><code>mycomputer$@ANITBLOG.NO</code></em>”.</p>

<p>The user principal name is what would be used during the user identification part of the authentication process (<em><code>AS_REQ/AS_REP</code></em>).</p>

<h5 id="service-principal" id="service-principal">Service Principal</h5>

<p>The “<em>service princpal name</em>” (SPN) is what identfies a service in the Kerberos authentication process (<em><code>TGS_REQ</code></em><code>/</code><em><code>TGS_</code></em><code>REP</code>).</p>

<p>In AD DS this is defined in the AD-object attribute <code>_servicePrincipalName_</code>.<br>
This is a multi string attribute. And can contain several identifiers.</p>

<p>When the KDC gets a request for a service ticket it will look for a SPN matching the request.<br>
If it finds a match it will send back a service ticket encrypted with the credentials of the AD-object the that has the SPN assigned.<br>
If the KDC is unable to find a SPN matching the request, then the authentication fails.</p>

<p>The above is extremely important to note and is why Kerberos often fails!<br>
For Kerberos to work the correct SPN <strong>must</strong> be found!</p>

<p>Service principals are usually written as <code>&lt;service&gt;/&lt;host&gt;</code> so for a http (or https) server in my example realm the SPN would be something like <code>http/webserver.anitblog.no</code>.<br>
The full service principal name would then be: <code>http/webserver.anitblog.no@ANITBLOG.NO</code>.</p>

<p>Also remember that this is reliant on name resolution, so if you access the server with only the NetBIOS name you would also need a entry for <code>http/webserver</code> too!</p>

<p>Herein also lies another stumbling block; the DNS name resolution.<br>
If you use a <code>A</code>/<code>AAAA</code> record to reach your server all is well, the SPN gets resolved correctly.<br>
However if you use a <code>CNAME</code> then you might get issues.</p>

<p>If in our case <code>webserver.anitblog.no</code> is a alias for <code>iishost.anitblog.no</code>, then the SPN that will be requested will most likely be for <code>http/iishost.anitblog.no</code> instead, which might fail depending on configured SPN!</p>

<p>Usually the simplest is to avoid <code>CNAME</code> records and use <code>A</code>/<code>AAAA</code> records instead.<br>
Depending on the use case you might simply add the SPN needed to the host server.</p>

<p>Or in case of load balancing; you would use a service account with the correct SPN running the service itself, and have the DNS record resolve to the load balancer.</p>

<p>The balancer then redirects the traffic to the back-end servers which can decrypt the service tickets using the service account credentials.<br>
Usually no authentication should be handled on the balancer itself, but there might be scenarios where this is needed.<br>
But that would be out of scope for this guide.</p>

<h6 id="the-host-spn" id="the-host-spn">The “HOST” SPN</h6>

<p>Note that there is a special type of service called <em><code>HOST</code></em>. This is actually a alias for several known service types.</p>

<p>The alias is defined in the <em><code>sPNMappings</code></em> attribute on the <em>Directory Service</em> object found at:</p>

<pre><code>&#34;CN=Directory Service,CN=Windows NT,CN=Services,CN=Configuration,DC=EXAMPLE,DC=COM&#34;. 
</code></pre>

<p>In general unless you have a very good reason you should avoid messing with this attribute.<br>
But it is useful to be aware of this.</p>

<p>Also if a specific entry is made for one of the services HOST is an alias for, then the specific entry will be used and the HOST alias is ignored.</p>

<pre><code>The SPN search pattern is basically something like this (using http/ as an example):
KDC looks for &#34;http/&lt;host&gt;&#34; -&gt; &#34;http/&lt;host&gt;&#34; found on object -&gt; All good!

KDC looks for &#34;http/&lt;host&gt;&#34; -&gt; &#34;http/&lt;host&gt;&#34; not found -&gt; substituting &#34;http/&lt;host&gt;&#34; with &#34;HOST/&lt;host&gt;&#34; -&gt; &#34;HOST/&lt;host&gt;&#34; found -&gt; All Good!

KDC looks for &#34;http/&lt;host&gt;&#34; -&gt; &#34;http/&lt;host&gt;&#34; not found -&gt; substituting &#34;http/&lt;host&gt;&#34; with &#34;HOST/&lt;host&gt;&#34; -&gt; &#34;HOST/&lt;host&gt;&#34; not found -&gt; Fail!  
</code></pre>

<h3 id="working-with-service-principal-names" id="working-with-service-principal-names">Working with service principal names</h3>

<p>So now we know a bit on the authentication process for Kerberos.<br>
Lets take a closer look at SPN that are required for all this to work.</p>

<h4 id="modifying-spn" id="modifying-spn">Modifying SPN</h4>

<p>As noted earlier the SPN are defined in the AD attribute <code>servicePrincipalName</code> this means that <em>any</em> method of editing AD attributes of a object is valid for for modifying SPN.</p>

<p>The most commonly used way is to use the tool <code>setspn.exe</code> but it is by no means the only method (<a href="https://ss64.com/nt/setspn.html">here is a reference for the tool</a>).</p>

<p>You can also use the <a href="https://docs.microsoft.com/en-us/powershell/module/addsadministration/">Active Directory PowerShell cmdlets</a>, for example by using parameter <code>-ServicePrincipalName</code>.<br>
Or you can modify it using the PowerShell accelerator <code>[ADSI]</code>.</p>

<p>You can of course also edit it using GUI tools like “AD Users and Computers”, “ADSI Edit” or even <code>ldp.exe</code>.</p>

<p>Using <code>setspn.exe</code> is pretty straight forward like this:</p>

<pre><code>This will assign the SPN &#34;http/webhost.anitblog.no&#34; to the AD object  &#34;iishost&#34; 

setspn.exe -s http/webhost.anitblog.no iishost
</code></pre>

<p>Adding SPN using the PowerShell cmdlets:</p>

<p>This will assign the SPN “http/webhost.anitblog.no” to the AD computer “iishost”</p>

<p>Set-ADComputer -Identity “iishost$” -ServicePrincipalNames (@{Add=“http/webhost.anitblog.no”})</p>

<p>Note that we are using the <code>sAMAccountName</code> here so we add a $ at the end of the identity.</p>

<p>While only showing for the cmdlet <code>ADComputer</code>, process is similar on <code>ADUser</code>and <code>ADServiceAccount</code>.</p>

<p><code>ADObject</code> cmdlets require a bit more effort to use but they could allow you to make a script that works regardless of AD object type.</p>

<pre><code>Here we first need to find the object using a Get-ADObject filter.
Then we pipe it to Set-ADObject using the parameter -Add to add our SPN to the servicePrincipalName attribute.

Get-ADObject -Filter &#34;sAMAccountName -eq &#39;iishost$&#39;&#34; | Set-ADObject -Add @{servicePrincipalName=&#34;http/webhost.anitblog.no&#34;}
</code></pre>

<p>Using ADSI can be a bit difficult but nonetheless possible as well:</p>

<pre><code>Create the ADSI object

[ADSI]$ADObject = (([adsisearcher]&#39;sAMAccountName=iishost$&#39;).FindOne()).Path

Append SPN to the object

$ADObject.PutEx(&#39;3&#39;, &#39;servicePrincipalName&#39;, @(&#39;http/webhost.anitblog.no&#39;))

Write info back to directory

$ADObject.SetInfo()
</code></pre>

<p>Working with ADSI is pretty low-level as you are working directly with .net classes.<br>
You rarely will need to do this, but it should always be available on any Windows computer past Windows 7.</p>

<p>Some more details on the <code>PutEx</code> method can be found in <a href="https://docs.microsoft.com/en-us/windows/win32/api/iads/nf-iads-iads-putex">MS Docs</a>.<br>
Also here is a quick reference the <code>PutEx</code> actions:<br>
<code>CLEAR = 1   UPDATE = 2   APPEND = 3   DELETE = 4</code></p>

<p>In general most methods will fail if you try to create duplicate SPN.<br>
But some tools might misbehave so it can still happen....</p>

<h4 id="finding-spn" id="finding-spn">Finding SPN</h4>

<p>In any case it is useful to know some commands to check if an SPN has been set, so here are a few ways to do that.<br>
If you are trying just to quickly check if a SPN is defined use you can use:</p>

<pre><code>setspn.exe -Q &lt;SPN&gt;
</code></pre>

<p>Using PowerShell cmdlet <code>Get-ADObject</code> you can do it like this (this also support wildcards):</p>

<pre><code>Get-ADObject -Filter &#39;servicePrincipalName -eq &#34;http/webserver.anitblog.no&#34;&#39; -Properties Name,servicePrincipalName
</code></pre>

<p>And using <code>[ADSISearcher]</code>:</p>

<pre><code>([adsisearcher]&#39;servicePrincipalName=http/webserver.anitblog.no&#39;).FindAll()
</code></pre>

<p>If you are simply trying to find any duplicates while troubleshooting the most simple way is:</p>

<pre><code>setspn.exe -X
</code></pre>

<p>If you are automating I would strongly recommend using either the PowerShell cmdlets or if the AD-cmdlets are not installed, use the accelerators (which should always be available from Win 7 and onwards).
These will make it easier to handle errors since they will output native .net objects or exit codes you can use.
If you were to use <code>setspn.exe</code> you would have to parse the string output.</p>

<h3 id="putting-it-all-into-use" id="putting-it-all-into-use">Putting it all into use</h3>

<p>Now we should have a decent grasp of the requirements on the KDC side of things so lets move on practicals.<br>
I am not going to dive very deep into this, as that would warrant it&#39;s own article. But we will go over the basics.</p>

<h4 id="clients" id="clients">Clients</h4>

<p>On the client side; if your Windows client is in the domain, no further configuration is needed to use Kerberos.<br>
All of this is handled in the background.</p>

<p>On MacOS or Linux (or Windows client in a workgroup) you will have to configure you Kerberos client to be able to get valid tickets from the Kerberos realm.<br>
In most cases this involves invoking <code>[kinit](https://web.mit.edu/Kerberos/krb5-latest/doc/user/user_commands/kinit.html)</code>, or using <code>[ksetup](https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/ksetup)</code> on Windows.<br>
Of course it is possible to use other Kerberos implementations on Windows depending on requirements as well.</p>

<pre><code>Also to note: Microsoft has expanded Kerberos with something called &#34;Service4User&#34; (S4U).
This is also often referred to as protocol transition.
This extension allows a service to contact a KDC on behalf of a client if they can provide the correct credentials through other means.
This can be by for example; username and password (basic, NTLM) or client certificate.
Details on the extension can be found here.
</code></pre>

<h4 id="servers" id="servers">Servers</h4>

<p>Servers have pretty much the same requirements as clients.<br>
They need to have a working Kerberos client, so if your Windows Servers are joined to the domain, you are all good.</p>

<p>For other platforms you would usually need to create Kerberos keytabs to store their credentials, and use those with for example <code>kinit</code>.<br>
In AD DS, keytab creation is done with <code>[ktpass](https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/ktpass)</code>, for other platforms this is usually done with the tool <code>[ktadd](https://web.mit.edu/Kerberos/krb5-latest/doc/admin/appl_servers.html)</code>.</p>

<pre><code>VERY IMPORTANT!It cannot be stressed enough but a keytab is very much the equivalent of storing a username and password in a clear text file.And as such it must be protected in the same way (absolutely do not send it by e-mail for example).
</code></pre>

<p>For your services; as long as you assign the correct SPN to the account running your service (computer, user or service account).<br>
Or the server running the service can access the credentials on behalf of the account with the SPN, things should <em>just work</em>™ (mostly depending on the service itself)....</p>

<h2 id="summary" id="summary">Summary</h2>

<p>So to summary this very long article; while Kerberos itself is a complex beast, using it needs not be so.</p>

<p>In your AD DS all the pieces are already in place, so the most important detail is simply to set the SPN correctly.</p>

<p>Second important part is to that pay attention to how the service names gets resolved by the DNS.<br>
This is a vital part of how the client figures out just which service they are requesting access to (so be careful when using <code>CNAME</code>).</p>

<p>But before we leave I have a few points that needs to be covered.</p>

<h4 id="iis-negotiate" id="iis-negotiate">IIS Negotiate</h4>

<p>On a IIS server with <code>Windows Authentication</code> it is strongly recommended to only have <code>Negotiate</code> as the provider, unless you have a specific specific issue.<br>
The HTTP Auth Negotiate method is implemented in most http clients (like in Chromium as mentioned earlier), and it will negotiate the use of strongest available authentication method.</p>

<p>Usually this means that Kerberos will first be attempted, and if that fails it will fall back to NTLM.</p>

<p>You can also force the use of Kerberos by using <code>Negotiate:Kerberos</code> if the service should only be available when authenticated with Kerberos.</p>

<h4 id="kerberos-encryption-ciphers" id="kerberos-encryption-ciphers">Kerberos encryption ciphers</h4>

<p>In short Kerberos can work using several encryption types.<br>
For a AD DS these are:<br>
<code>DES_CBC_CRC</code><br>
<code>DES_CBC_MD5</code><br>
<code>RC4_HMAC_MD5</code><br>
<code>AES128_HMAC_SHA1</code><br>
<code>AES256_HMAC_SHA1</code></p>

<p>Of these it is only recommended to use the AES ones.<br>
The DES type are disabled by default in most versions of AD DS, but RC4 is still enabled by default.<br>
It is strongly recommended that the RC4 cipher is disabled and that only AES types are allowed.<br>
<a href="https://docs.microsoft.com/en-us/windows-server/security/kerberos/preventing-kerberos-change-password-that-uses-rc4-secret-keys">Here is Microsoft&#39;s information on this</a>.</p>

<p>When you are working on disabling RC4 pay close attention if the domain is part of a forest.<br>
In that case the domain trust must be marked as supporting AES as well.<br>
<a href="https://support.microsoft.com/en-us/help/4492348/kerberos-unsupported-etype-error-when-authenticating-across-trust">Microsoft has a KB on this here</a>.</p>

<h4 id="comments" id="comments">Comments</h4>

<p>While I have have only glanced by some points, I hope this will be a helpful primer on understanding and fully utilizing Kerberos.</p>

<p>As always; please comment if there is something that incorrect or unclear. I&#39;ll be happy to correct it.</p>
]]></content:encoded>
      <guid>https://anitblog.no/kerberos-a-guide</guid>
      <pubDate>Fri, 06 Sep 2024 16:03:36 +0000</pubDate>
    </item>
    <item>
      <title>DFS resolution headache redux</title>
      <link>https://anitblog.no/dfs-resolution-headache-redux</link>
      <description>&lt;![CDATA[  Originally posted on 2019-06-14 on the old blog&#xA;&#xA;This is a followup on my post DFS domain name resolution headaches.&#xA;&#xA;Occasionally there are times when the solution seems correct since it fixes the issue there and then. But then it turns out you where looking in the entirely wrong place.....&#xA;&#xA;!--more--&#xA;&#xA;Turns out the issue was the rather common issue of using DFS together with Offline files.  &#xA;If Offline files is enabled and Windows detects a &#34;poor connection&#34; it will automatically go into Slow-Link mode which drops all connection/access to the DFS share beyond those files that have been selected for caching (selected either automatically or by the user).&#xA;&#xA;The simple solution to this is to configure the &#34;Slow-Link&#34; setting in a computer configuration GPO.  &#xA;By default slow-link will initiate at 80ms on Win7 and 35ms on Win8 and onwards.  &#xA;Preferably you should set your DFS root to a high latency (like a 1000ms). Then you can for example set the user network folder to something like 50ms or 100ms.  &#xA;For example like this:&#xA;&#xA;\\example.com\DFSRoot Latency=1000&#xA;\\example.com\DFSRoot\Users Latency=100&#xA;&#xA;Now that the solution is found it seems obvious, and it also seems strange that the cause was not discovered earlier.&#xA;&#xA;However this is is where the !fun is; usually only you laptops would have enabled Offline files (desktops are usually always connected to the network), and indeed that is how it looked in our GPO... that is filtering with a WMI query for laptops.  &#xA;So why did we also have desktops with this same issue?  &#xA;Well... don&#39;t let your colleagues check the computer form factor by simply query kind of RAM form factor is used, desktops too can have SO-DIMM modules.... 🤨&#xA;&#xA;So what have i learned from this?  &#xA;\- If you have any issues at all with DFS connections, verify your Offline files configuration. And remember to configure Slow-Link.  &#xA;\- Even if the GPO WMI filter is named &#34;Laptops Only&#34;, does not make it true.  &#xA;Select * from Win32_PhysicalMemory WHERE (FormFactor = 12) do NOT a laptop make....&#xA;]]&gt;</description>
      <content:encoded><![CDATA[<blockquote><p>Originally posted on 2019-06-14 on the old blog</p></blockquote>

<p>This is a followup on my post <a href="https://www.anitblog.no/software/dfs-domain-name-resolution-headaches/">DFS domain name resolution headaches</a>.</p>

<p>Occasionally there are times when the solution seems correct since it fixes the issue there and then. But then it turns out you where looking in the entirely wrong place.....</p>



<p>Turns out the issue was the rather common issue of using DFS together with Offline files.<br>
If Offline files is enabled and Windows detects a “poor connection” it will automatically go into Slow-Link mode which drops all connection/access to the DFS share beyond those files that have been selected for caching (selected either automatically or by the user).</p>

<p>The simple solution to this is to configure the “Slow-Link” setting in a computer configuration GPO.<br>
By default slow-link will initiate at 80ms on Win7 and 35ms on Win8 and onwards.<br>
Preferably you should set your DFS root to a high latency (like a 1000ms). Then you can for example set the user network folder to something like 50ms or 100ms.<br>
For example like this:</p>

<pre><code>\\example.com\DFSRoot Latency=1000
\\example.com\DFSRoot\Users Latency=100
</code></pre>

<p>Now that the solution is found it seems obvious, and it also seems strange that the cause was not discovered earlier.</p>

<p>However this is is where the !fun is; usually only you laptops would have enabled Offline files (desktops are usually always connected to the network), and indeed that is how it looked in our GPO... that is filtering with a WMI query for laptops.<br>
So why did we also have desktops with this same issue?<br>
Well... don&#39;t let your colleagues check the computer form factor by simply query kind of RAM form factor is used, desktops too can have SO-DIMM modules.... 🤨</p>

<p>So what have i learned from this?<br>
- If you have any issues at all with DFS connections, verify your Offline files configuration. And remember to configure Slow-Link.<br>
- Even if the GPO WMI filter is named “Laptops Only”, does not make it true.<br>
<code>Select * from Win32_PhysicalMemory WHERE (FormFactor = 12)</code> do NOT a laptop make....</p>
]]></content:encoded>
      <guid>https://anitblog.no/dfs-resolution-headache-redux</guid>
      <pubDate>Fri, 06 Sep 2024 16:03:24 +0000</pubDate>
    </item>
    <item>
      <title>DFS domain name resolution headaches</title>
      <link>https://anitblog.no/dfs-domain-name-resolution-headaches</link>
      <description>&lt;![CDATA[  Originally posted on 2018-12-05 on the old blog&#xA;&#xA;EDIT!: An update to this six months later....&#xA;&#xA;So here is a quick one; ever had issues with DFS (Distributed File System) share being unable to resolve their name properly at seemingly random times?&#xA;&#xA;If the answer is yes, here is a quick solution to test: Try appending a &#34;.&#34; (dot) at the end of the fully qualified domain. So \\anitblog.no\DFSRoot would become \\anitblog.no.\DFSRoot.&#xA;&#xA;The reason for this is that appending the dot to a FQDN makes it an absolute query instead of an relative one.&#xA;&#xA;Instead of rephrasing someone else, here is an explanation about dots in name resolution: https://stackoverflow.com/questions/19480767/domain-names-with-dots-at-the-end&#xA;]]&gt;</description>
      <content:encoded><![CDATA[<blockquote><p>Originally posted on 2018-12-05 on the old blog</p></blockquote>

<p>EDIT!: <a href="https://www.anitblog.no/software/dfs-resolution-headache-redux">An update to this six months later....</a></p>

<p>So here is a quick one; ever had issues with DFS (Distributed File System) share being unable to resolve their name properly at seemingly random times?</p>

<p>If the answer is yes, here is a quick solution to test: Try appending a “.” (dot) at the end of the fully qualified domain. So <code>\\anitblog.no\DFSRoot</code> would become <code>\\anitblog.no.\DFSRoot</code>.</p>

<p>The reason for this is that appending the dot to a FQDN makes it an absolute query instead of an relative one.</p>

<p>Instead of rephrasing someone else, here is an explanation about dots in name resolution: <a href="https://stackoverflow.com/questions/19480767/domain-names-with-dots-at-the-end">https://stackoverflow.com/questions/19480767/domain-names-with-dots-at-the-end</a></p>
]]></content:encoded>
      <guid>https://anitblog.no/dfs-domain-name-resolution-headaches</guid>
      <pubDate>Fri, 06 Sep 2024 16:03:12 +0000</pubDate>
    </item>
    <item>
      <title>Drivers - Thunderbolt Software, device approval without local admin</title>
      <link>https://anitblog.no/drivers-thunderbolt-software-device-approval-without-local-admin</link>
      <description>&lt;![CDATA[  Originally posted on 2018-11-09 on the old blog&#xA;&#xA;I stumbled into an issue recently with Thunderbolt enabled computers.&#xA;&#xA;By default the Thunderbolt Software that is used to approve Thunderbolt devices requires local administrator to work, this is not really practical in enterprise environments where most users are not local administrators.&#xA;&#xA;So i dug into it and found some solutions to this issue.&#xA;&#xA;!--more--&#xA;&#xA;Solution 1 - Disable User Authorization&#xA;&#xA;The simplest solution is to go into your device BIOS and change your Thunderbolt settings from &#34;User Authorization&#34; to &#34;No Security&#34; (exact wording varies depending on device).&#xA;&#xA;This however is not really recommended as you would be compromising the security of the device.&#xA;&#xA;By doing this you are essentially exposing a PCIe bus to the outer world without any form of security, enabling easy access for a DMA attack. Though there are use cases where this might be useful nonetheless....&#xA;&#xA;Solution 2 - Disable local admin requirement&#xA;&#xA;The second and recommended solution is to simply disable the requirement for a administrator to approve Thunderbolt devices.&#xA;&#xA;In simplest terms it comes down to changing the registry property &#34;ApprovalLevel&#34; value to &#34;1&#34; under the registry key:&#xA;&#xA;HKEYLOCALMACHINE\SYSTEM\CurrentControlSet\Services\ThunderboltService\TbtServiceSettings&#xA;&#xA;However this is not as straight forward as it would seem, as this key has some very restrictive permissions. Basically only the Thunderbolt service can change it, so even a local admin would not be able to change it normally.&#xA;&#xA;Now there are two ways to solve this: Now there are three ways to solve this:&#xA;&#xA;Solution 2A - Reinstall application&#xA;&#xA;The first solution out there is easily available, it basically boils down to this: - Uninstall the Thunderbolt Software - Reboot - Change registry property - Reboot (optional but recommended) - Install Thunderbolt Software - Connect all the devices you want&#xA;&#xA;This is a straight forward if cumbersome way to handle it (especially if you have to do this on several computers).&#xA;&#xA;I have also had one issue with this that the installer actually created a new key with the exact same name (which i did not think was even possible)!&#xA;&#xA;To make things easier for me i developed a PowerShell script to change this registry key, so on to the next solution!&#xA;&#xA;Solution 2B - Script your way out&#xA;&#xA;So after butting heads with the keyboard for a while i came up with a scripted way to solve this.&#xA;&#xA;In summary the script is doing these actions. - Backup original ACL settings for the key in question. - Using .Net methods apply full access for &#34;NT Authority\\SYSTEM&#34; account on the key. - Change the property in registry. - Reset ACL based on backup. - Pack script in a Scriptblock and encode it in a Base64 string - Create a Scheduled Task with the encoded command as action launched by PowerShell, and SYSTEM as the running user (with high access). - Run task immediately - Delete task afterwards&#xA;&#xA;Change Thundebolt Software approval level to allow users to accept Thundebolt equpiment without being local admin&#xA;&#xA;Created by Jens-Kristian Myklebust jensmyklebust@outlook.com&#xA;Create Script payload&#xA;$TBFix ={&#xA;&#xA;    #Registry key to modify&#xA;    $RegistryKey = &#34;SYSTEM\CurrentControlSet\Services\ThunderboltService\TbtServiceSettings&#34;&#xA;    &#xA;    #Store original ACL info&#xA;    $ACLinfo = Get-Acl &#34;HKLM:\$RegistryKey&#34;&#xA;    &#xA;    #Modify ACL using .Net methods, and give &#34;SYSTEM&#34; user full access to registry key&#xA;    $RegKeyDotNETItem = [Microsoft.Win32.Registry]::LocalMachine.OpenSubKey($RegistryKey,[Microsoft.Win32.RegistryKeyPermissionCheck]::ReadWriteSubTree,[System.Security.AccessControl.RegistryRights]::ChangePermissions)&#xA;    $DotNETACL = $RegKeyDotNETItem.GetAccessControl()&#xA;    $DotNETAccessRule = New-Object System.Security.AccessControl.RegistryAccessRule (&#34;System&#34;,&#34;FullControl&#34;,&#34;Allow&#34;)&#xA;    $DotNETACL.SetAccessRule($DotNETAccessRule)&#xA;    $RegKeyDotNETItem.SetAccessControl($DotNETACL)&#xA;    &#xA;    #Change property of Item in key to desired value&#xA;    Set-ItemProperty -Path &#34;HKLM:\$RegistryKey&#34; -Name &#39;ApprovalLevel&#39; -Value 1&#xA;    &#xA;    #Reset ACL to what it was before modification&#xA;    Set-Acl -AclObject $ACLinfo -Path &#34;HKLM:\$RegistryKey&#34;&#xA;    &#xA;}&#xA;&#xA;Encode payload script in a base64 string&#xA;$TBFixEncoded = [Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes($TBFix))&#xA;&#xA;Create action for scheduled task&#xA;$action = New-ScheduledTaskAction -Execute powershell.exe -Argument &#34;-noprofile -encodedcommand $TBFixEncoded&#34;&#xA;Create task&#xA;$Taskname = &#34;Run Thundebolt User Access Fix&#34;&#xA;Register-ScheduledTask -Action $action -TaskName $Taskname -RunLevel Highest -User S-1-5-18 -Force&#xA;Run task &#xA;Start-ScheduledTask -TaskName $Taskname&#xA;Remove task&#xA;Unregister-ScheduledTask -TaskName $Taskname -Confirm:$false&#xA;&#xA;Running this script as admin will put the change into effect immediately. If the Thunderbolt Software is already running you might need to restart it or the computer for properly pick it up the change.&#xA;&#xA;Solution 2C - Set registry property before installing (new!)&#xA;&#xA;This should have been really obvious when i wrote this, but I did not think of it then.&#xA;&#xA;If you are doing fresh deployments you can also add the registry property before installing the Thunderbolt Software.&#xA;&#xA;Exactly how to implement this depends a bit on your solution. But in the case of SCCM; running this in a Command Line step should do the trick (again remember to do this before installing the TB software):&#xA;&#xA;cmd /c reg add &#34;HKLM\SYSTEM\CurrentControlSet\Services\ThunderboltService\TbtServiceSettings&#34; /v ApprovalLevel /t REGDWORD /d 1 /f&#xA;&#xA;(A shout-out to my colleague Ilvars for being the one that actually implements my barrage of suggestions!)&#xA;&#xA;I hope this solution will be helpful for those who stumble upon the same issues as i have.&#xA;]]&gt;</description>
      <content:encoded><![CDATA[<blockquote><p>Originally posted on 2018-11-09 on the old blog</p></blockquote>

<p>I stumbled into an issue recently with Thunderbolt enabled computers.</p>

<p>By default the Thunderbolt Software that is used to approve Thunderbolt devices requires local administrator to work, this is not really practical in enterprise environments where most users are not local administrators.</p>

<p>So i dug into it and found some solutions to this issue.</p>



<h3 id="solution-1-disable-user-authorization" id="solution-1-disable-user-authorization">Solution 1 – Disable User Authorization</h3>

<p>The simplest solution is to go into your device BIOS and change your Thunderbolt settings from “User Authorization” to “No Security” (exact wording varies depending on device).</p>

<p>This however is not really recommended as you would be compromising the security of the device.</p>

<p>By doing this you are essentially exposing a PCIe bus to the outer world without any form of security, enabling easy access for a DMA attack. Though there are use cases where this might be useful nonetheless....</p>

<h3 id="solution-2-disable-local-admin-requirement" id="solution-2-disable-local-admin-requirement">Solution 2 – Disable local admin requirement</h3>

<p>The second and recommended solution is to simply disable the requirement for a administrator to approve Thunderbolt devices.</p>

<p>In simplest terms it comes down to changing the registry property “ApprovalLevel” value to “1” under the registry key:</p>

<pre><code class="language-batch">HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\ThunderboltService\TbtServiceSettings
</code></pre>

<p>However this is not as straight forward as it would seem, as this key has some very restrictive permissions. Basically only the Thunderbolt service can change it, so even a local admin would not be able to change it normally.</p>

<p>Now there are two ways to solve this: Now there are three ways to solve this:</p>

<h4 id="solution-2a-reinstall-application" id="solution-2a-reinstall-application">Solution 2A – Reinstall application</h4>

<p>The first solution out there is easily available, it basically boils down to this: – Uninstall the Thunderbolt Software – Reboot – Change registry property – Reboot (optional but recommended) – Install Thunderbolt Software – Connect all the devices you want</p>

<p>This is a straight forward if cumbersome way to handle it (especially if you have to do this on several computers).</p>

<p>I have also had one issue with this that the installer actually created a new key with the exact same name (which i did not think was even possible)!</p>

<p>To make things easier for me i developed a PowerShell script to change this registry key, so on to the next solution!</p>

<h4 id="solution-2b-script-your-way-out" id="solution-2b-script-your-way-out">Solution 2B – Script your way out</h4>

<p>So after butting heads with the keyboard for a while i came up with a scripted way to solve this.</p>

<p>In summary the script is doing these actions. – Backup original ACL settings for the key in question. – Using .Net methods apply full access for “NT Authority\SYSTEM” account on the key. – Change the property in registry. – Reset ACL based on backup. – Pack script in a Scriptblock and encode it in a Base64 string – Create a Scheduled Task with the encoded command as action launched by PowerShell, and SYSTEM as the running user (with high access). – Run task immediately – Delete task afterwards</p>

<pre><code class="language-powershell">#Change Thundebolt Software approval level to allow users to accept Thundebolt equpiment without being local admin

#Created by Jens-Kristian Myklebust &lt;jensmyklebust@outlook.com&gt;
#Create Script payload
$TBFix ={

    #Registry key to modify
    $RegistryKey = &#34;SYSTEM\CurrentControlSet\Services\ThunderboltService\TbtServiceSettings&#34;
    
    #Store original ACL info
    $ACLinfo = Get-Acl &#34;HKLM:\$RegistryKey&#34;
    
    #Modify ACL using .Net methods, and give &#34;SYSTEM&#34; user full access to registry key
    $RegKeyDotNETItem = [Microsoft.Win32.Registry]::LocalMachine.OpenSubKey($RegistryKey,[Microsoft.Win32.RegistryKeyPermissionCheck]::ReadWriteSubTree,[System.Security.AccessControl.RegistryRights]::ChangePermissions)
    $DotNET_ACL = $RegKeyDotNETItem.GetAccessControl()
    $DotNET_AccessRule = New-Object System.Security.AccessControl.RegistryAccessRule (&#34;System&#34;,&#34;FullControl&#34;,&#34;Allow&#34;)
    $DotNET_ACL.SetAccessRule($DotNET_AccessRule)
    $RegKeyDotNETItem.SetAccessControl($DotNET_ACL)
    
    #Change property of Item in key to desired value
    Set-ItemProperty -Path &#34;HKLM:\$RegistryKey&#34; -Name &#39;ApprovalLevel&#39; -Value 1
    
    #Reset ACL to what it was before modification
    Set-Acl -AclObject $ACLinfo -Path &#34;HKLM:\$RegistryKey&#34;
    
}

#Encode payload script in a base64 string
$TBFixEncoded = [Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes($TBFix))


#Create action for scheduled task
$action = New-ScheduledTaskAction -Execute powershell.exe -Argument &#34;-noprofile -encodedcommand $TBFixEncoded&#34;
#Create task
$Taskname = &#34;Run Thundebolt User Access Fix&#34;
Register-ScheduledTask -Action $action -TaskName $Taskname -RunLevel Highest -User S-1-5-18 -Force
#Run task 
Start-ScheduledTask -TaskName $Taskname
#Remove task
Unregister-ScheduledTask -TaskName $Taskname -Confirm:$false
</code></pre>

<p>Running this script as admin will put the change into effect immediately. If the Thunderbolt Software is already running you might need to restart it or the computer for properly pick it up the change.</p>

<h4 id="solution-2c-set-registry-property-before-installing-new" id="solution-2c-set-registry-property-before-installing-new">Solution 2C – Set registry property before installing (new!)</h4>

<p>This should have been really obvious when i wrote this, but I did not think of it then.</p>

<p>If you are doing fresh deployments you can also add the registry property before installing the Thunderbolt Software.</p>

<p>Exactly how to implement this depends a bit on your solution. But in the case of SCCM; running this in a Command Line step should do the trick (again remember to do this before installing the TB software):</p>

<pre><code class="language-batch">cmd /c reg add &#34;HKLM\SYSTEM\CurrentControlSet\Services\ThunderboltService\TbtServiceSettings&#34; /v ApprovalLevel /t REG_DWORD /d 1 /f
</code></pre>

<p>(A shout-out to my colleague Ilvars for being the one that actually implements my barrage of suggestions!)</p>

<p>I hope this solution will be helpful for those who stumble upon the same issues as i have.</p>
]]></content:encoded>
      <guid>https://anitblog.no/drivers-thunderbolt-software-device-approval-without-local-admin</guid>
      <pubDate>Fri, 06 Sep 2024 16:03:00 +0000</pubDate>
    </item>
    <item>
      <title>PowerShell - Working with CIM - Remoting</title>
      <link>https://anitblog.no/powershell-working-with-cim-remoting</link>
      <description>&lt;![CDATA[  Originally posted on 2018-08-08 on the old blog&#xA;&#xA;Running your commands on a local machine is all well and good, but most admins work with remote servers.&#xA;&#xA;So lets talk about using CIM over network and using CIM sessions! !--more--&#xA;&#xA;CIM sessions&#xA;&#xA;Using CIM remotely involves working with the concept of CIM sessions. CIM sessions is similar to PowerShell sessions in that they are based on WsMan for connection, with the difference being that a CIM session is limited to CIM commands only. For reference WMI works with the legacy system DCOM instead.&#xA;&#xA;Some of the advantages compared to WMI is that you can re-use sessions, and you will only need to open the firewall ports required for WsMan (which reduces attack surface which is good in these security conscious days). It also has the advantage of being able to execute in parallel which can speed things up significantly at scale.&#xA;&#xA;Using CIM Sessions&#xA;&#xA;CIM sessions can be made in two ways: You can create a temporary one by specifying -ComputerName in Get-CimInstance (This is quick and easy):&#xA;&#xA;Get-CimInstance -ComputerName &#34;COMPUTER1&#34; -Namespace namespace -ClassName class&#xA;&#xA;Or you can create a session that is open until you close it, by using  New-CimSession to create, and Remove-CimSession to close:&#xA;&#xA;Create a session named &#34;Session1&#34;&#xA;New-CimSession -Name Session1 -ComputerName &#34;COMPUTER1&#34; &#xA;&#xA;Remove Cim &#34;session1&#34;&#xA;Remove-CimSession -CimSession Session1&#xA;&#xA;There are advantages and disadvantages to both.&#xA;&#xA;Usually if you only need to run one command against your targets then -ComputerName would the the best choice, it starts and stops the sessions automatically and is straight forward and easy to use. But it can be slow when you want to run many commands against the same target.&#xA;&#xA;The CIM session cmdlets show their power when you need to run multiple commands against your remote targets, since you only create the session once this can significantly speed up your script. Depending on the script this can actually shave off seconds off processing for each remote target. It does require you to pay a bit more attention though on your script. Sessions should close when you close the shell, but if your script exits out into a shell it is possible the connections are kept open.&#xA;&#xA;That&#39;s basically it for how CIM sessions works in practice, but lets show a real example of how this can be used.&#xA;&#xA;Array of hostnames&#xA;$computerlist = ,&#xA;&#34;Computer1&#34;,&#xA;&#34;Computer2&#34;&#xA;&#xA;Create empty array for use with successful connections&#xA;$availableComputers = @()&#xA;&#xA;Foreach loop that runs connection test against hostnames in $computerlist&#xA;Then it adds the successful connections to the $availabelComputers array&#xA;Foreach ($computer in $computerlist){&#xA;    $Test = Test-Connection $computer -Quiet -Count 1 -TimeToLive 10&#xA;    if($Test -eq &#34;True&#34; ){&#xA;      &#34;Connection to $Computer&#34;&#xA;      $availableComputers += $computer&#xA;    }&#xA;    &#xA;    else{&#xA;      &#34;No connection to $computer&#34;&#xA;    }&#xA;}&#xA;&#xA;Creates named sessions for all computers in $availableComputers&#xA;New-CimSession -Name RDPEnable -ComputerName $availableComputers | Out-Null&#xA;&#xA;Store the class object for all computers with the session name RDPEnable&#xA;$CIMTerminalServiceSetting = Get-CimSession -Name RDPEnable | Get-CimInstance -Namespace root/cimv2/TerminalServices -ClassName Win32TerminalServiceSetting&#xA;$CIMTSGeneralSetting = Get-CimSession -Name RDPEnable | Get-CimInstance -Namespace root/cimv2/TerminalServices -ClassName Win32TSGeneralSetting&#xA; &#xA;Invoke CIM methods against the class object variables.&#xA;$CIMTerminalServiceSetting | Invoke-CimMethod -MethodName SetAllowTSConnections -Arguments @{AllowTSConnections=1;ModifyFirewallException=1} | Format-Table -Property PSComputerName,ReturnValue -AutoSize&#xA;$CIMTSGeneralSetting | Invoke-CimMethod -MethodName SetUserAuthenticationRequired -Arguments @{UserAuthenticationRequired=1} | Out-Null&#xA;&#xA;Remove all sessions with the name RDPEnable&#xA;Get-CimSession -Name RDPEnable | Remove-CimSession&#xA;&#xA;This builds on the previous post example regarding enabling RDP. Here you feed it a list of hostnames, then it will do a basic connection test before it creates a named session containing all the available computers. After that it uses the session to set the correct RDP flags.&#xA;&#xA;That&#39;s it for the basics of working with CIM cmdlets in PowerShell. For those that has taken the time to read my posts; i hope you have learned something new that you can use be it at work, the lab or at home.&#xA;&#xA;So until my next post; have a good one!&#xA;]]&gt;</description>
      <content:encoded><![CDATA[<blockquote><p>Originally posted on 2018-08-08 on the old blog</p></blockquote>

<p>Running your commands on a local machine is all well and good, but most admins work with remote servers.</p>

<p>So lets talk about using CIM over network and using CIM sessions! </p>

<h3 id="cim-sessions" id="cim-sessions">CIM sessions</h3>

<p>Using CIM remotely involves working with the concept of CIM sessions. CIM sessions is similar to PowerShell sessions in that they are based on WsMan for connection, with the difference being that a CIM session is limited to CIM commands only. For reference WMI works with the legacy system DCOM instead.</p>

<p>Some of the advantages compared to WMI is that you can re-use sessions, and you will only need to open the firewall ports required for WsMan (which reduces attack surface which is good in these security conscious days). It also has the advantage of being able to execute in parallel which can speed things up significantly at scale.</p>

<h4 id="using-cim-sessions" id="using-cim-sessions">Using CIM Sessions</h4>

<p>CIM sessions can be made in two ways: You can create a temporary one by specifying <code>-ComputerName</code> in <code>Get-CimInstance</code> (This is quick and easy):</p>

<pre><code class="language-powershell">Get-CimInstance -ComputerName &#34;COMPUTER1&#34; -Namespace &lt;namespace&gt; -ClassName &lt;class&gt;
</code></pre>

<p>Or you can create a session that is open until you close it, by using  <code>New-CimSession</code> to create, and <code>Remove-CimSession</code> to close:</p>

<pre><code class="language-powershell">#Create a session named &#34;Session1&#34;
New-CimSession -Name Session1 -ComputerName &#34;COMPUTER1&#34; 

#Remove Cim &#34;session1&#34;
Remove-CimSession -CimSession Session1
</code></pre>

<p>There are advantages and disadvantages to both.</p>

<p>Usually if you only need to run one command against your targets then <code>-ComputerName</code> would the the best choice, it starts and stops the sessions automatically and is straight forward and easy to use. But it can be slow when you want to run many commands against the same target.</p>

<p>The CIM session cmdlets show their power when you need to run multiple commands against your remote targets, since you only create the session once this can significantly speed up your script. Depending on the script this can actually shave off seconds off processing for each remote target. It does require you to pay a bit more attention though on your script. Sessions should close when you close the shell, but if your script exits out into a shell it is possible the connections are kept open.</p>

<p>That&#39;s basically it for how CIM sessions works in practice, but lets show a real example of how this can be used.</p>

<pre><code class="language-powershell">#Array of hostnames
$computerlist = ,
&#34;Computer1&#34;,
&#34;Computer2&#34;

#Create empty array for use with successful connections
$availableComputers = @()

#Foreach loop that runs connection test against hostnames in $computerlist
#Then it adds the successful connections to the $availabelComputers array
Foreach ($computer in $computerlist){
    $Test = Test-Connection $computer -Quiet -Count 1 -TimeToLive 10
    if($Test -eq &#34;True&#34; ){
      &#34;Connection to $Computer&#34;
      $availableComputers += $computer
    }
    
    else{
      &#34;No connection to $computer&#34;
    }
}

#Creates named sessions for all computers in $availableComputers
New-CimSession -Name RDPEnable -ComputerName $availableComputers | Out-Null

#Store the class object for all computers with the session name RDPEnable
$CIM_TerminalServiceSetting = Get-CimSession -Name RDPEnable | Get-CimInstance -Namespace root/cimv2/TerminalServices -ClassName Win32_TerminalServiceSetting
$CIM_TSGeneralSetting = Get-CimSession -Name RDPEnable | Get-CimInstance -Namespace root/cimv2/TerminalServices -ClassName Win32_TSGeneralSetting
 
#Invoke CIM methods against the class object variables.
$CIM_TerminalServiceSetting | Invoke-CimMethod -MethodName SetAllowTSConnections -Arguments @{AllowTSConnections=1;ModifyFirewallException=1} | Format-Table -Property PSComputerName,ReturnValue -AutoSize
$CIM_TSGeneralSetting | Invoke-CimMethod -MethodName SetUserAuthenticationRequired -Arguments @{UserAuthenticationRequired=1} | Out-Null

#Remove all sessions with the name RDPEnable
Get-CimSession -Name RDPEnable | Remove-CimSession
</code></pre>

<p>This builds on the previous post example regarding enabling RDP. Here you feed it a list of hostnames, then it will do a basic connection test before it creates a named session containing all the available computers. After that it uses the session to set the correct RDP flags.</p>

<p>That&#39;s it for the basics of working with CIM cmdlets in PowerShell. For those that has taken the time to read my posts; i hope you have learned something new that you can use be it at work, the lab or at home.</p>

<p>So until my next post; have a good one!</p>
]]></content:encoded>
      <guid>https://anitblog.no/powershell-working-with-cim-remoting</guid>
      <pubDate>Fri, 06 Sep 2024 16:02:50 +0000</pubDate>
    </item>
    <item>
      <title>PowerShell - Working with CIM - Methods</title>
      <link>https://anitblog.no/powershell-working-with-cim-methods</link>
      <description>&lt;![CDATA[  Originally posted on 2018-08-03 on the old blog&#xA;&#xA;Onward to working with CIM methods!&#xA;&#xA;The data you get from using Get-CIMInstance is rarely directly modifiable as the data returned is usually a snapshot of deeper system settings. To change this you will need to use the methods provided by the Class you are looking at.&#xA;&#xA;!--more--&#xA;&#xA;Note that that most classes have their own methods unique to them, so depending on what you want to do you will have to read up on the documentation for that class.&#xA;&#xA;There are ways to look up the method names themselves in PowerShell, but the format of the input data is not always obvious. Luckily in most cases you will find what you need for classes made by Microsoft at Microsoft Docs (just search for the class name).&#xA;&#xA;Example&#xA;&#xA;For this article i will mainly use the class Win32\TerminalServiceSettings as an example. This is the class that controls most of the RDP Host settings on the computer.&#xA;&#xA;Here is a snippet that will enable RDP on your local machine (run from an elevated shell):&#xA;&#xA;$Win32TerminalServiceSettings = Get-CimInstance -Namespace root/cimv2/TerminalServices -ClassName Win32TerminalServiceSetting &#xA;$Win32TerminalServiceSettings | Invoke-CimMethod -MethodName SetAllowTSConnections -Arguments @{AllowTSConnections=1;ModifyFirewallException=1}&#xA;&#xA;So lets break this down a bit.&#xA;&#xA;$Win32TerminalServiceSettings = Get-CimInstance -Namespace root/cimv2/TerminalServices -ClassName Win32TerminalServiceSetting&#xA;&#xA;This line simply creates a variable containing the CIM Class object. This makes it simple to reuse if you want to invoke several methods, it can also help improve readability of your script.&#xA;&#xA;$Win32TerminalServiceSettings | Invoke-CimMethod -MethodName SetAllowTSConnections -Arguments @{AllowTSConnections=1;ModifyFirewallException=1}&#xA;&#xA;Here we are getting to the meat. We pipe the class object variable into the Invoke-CimMethod cmdlet. Here we first specify the method &#34;SetAllowTSConnections&#34; with the -MethodName parameter. Then we set our arguments with -Arguments, this parameter uses a hashtable to set the properties &#34;AllowTSConnections&#34; to 1 (allow) and &#34;ModifyFirewallException&#34; to 1 (enable firewall rule).&#xA;&#xA;When you execute this command it will usually return a &#34;0&#34; to indicate success. You can check the setting by running the command:&#xA;&#xA;Get-CimInstance -Namespace root/cimv2/TerminalServices -ClassName Win32TerminalServiceSetting | select AllowTSConnections&#xA;&#xA;It is important to note that calling the variable instead will return stale data, as the variable contains a snapshot of the parameters at the time of creation. This can be useful to compare data, but it is something to be aware of when you check for change.&#xA;&#xA;Vs WMI-Object&#xA;&#xA;For those that are more familiar with WMI this could be done with this command:&#xA;&#xA;(Get-WmiObject -Namespace root/cimv2/TerminalServices -Class Win32TerminalServiceSetting).SetAllowTsConnections(0,0)&#xA;&#xA;This is an okay way to do it if your method does not have many parameters, but for some methods this can be really finicky.&#xA;&#xA;The reason for this is that the parameter is positional in WMI, meaning that if you have several parameters but you only want to change one you will have to find it&#39;s correct position to change it. This is not always easy, as occasionally the parameter position is different that what the documentation says.&#xA;&#xA;By using CIM methods you specify the name of the parameter in you arguments hashtable therefore rendering this issue moot.&#xA;&#xA;Conclusion&#xA;&#xA;As you can see above using CIM methods is fairly easy once you have the basics.&#xA;&#xA;And while you can probably write shorter code with WMI, this can lead to issues when trying to set parameters.&#xA;&#xA;My philosophy when writing scripts is that verbosity (within limits) is good, as it makes the script easier to read and understand and modify when necessary. And CIM cmdles are superior to WMI cmdlets in that way.&#xA;&#xA;Also if you want to use the snippet above to enable RDP, you would probably also want to ensure that &#34;Network Level Authenticaton&#34; is enabled as well. You can do this with this snippet:&#xA;&#xA;$Win32TSGeneralSettings = Get-CimInstance -Namespace root/cimv2/TerminalServices -ClassName Win32TSGeneralSetting&#xA;$Win32TSGeneralSettings| Invoke-CimMethod -MethodName SetUserAuthenticationRequired -Arguments @{UserAuthenticationRequired=1}&#xA;&#xA;Next article in the CIM series will talk about remote connections. Till then have a good one!&#xA;]]&gt;</description>
      <content:encoded><![CDATA[<blockquote><p>Originally posted on 2018-08-03 on the old blog</p></blockquote>

<p>Onward to working with CIM methods!</p>

<p>The data you get from using Get-CIMInstance is rarely directly modifiable as the data returned is usually a snapshot of deeper system settings. To change this you will need to use the methods provided by the Class you are looking at.</p>



<pre><code>Note that that most classes have their own methods unique to them, so depending on what you want to do you will have to read up on the documentation for that class.
</code></pre>

<p>There are ways to look up the method names themselves in PowerShell, but the format of the input data is not always obvious. Luckily in most cases you will find what you need for classes made by Microsoft at <a href="https://docs.microsoft.com">Microsoft Docs</a> (just search for the class name).</p>

<h2 id="example" id="example">Example</h2>

<p>For this article i will mainly use the class <a href="https://docs.microsoft.com/en-us/windows/desktop/termserv/win32-terminalservicesetting">Win32_TerminalServiceSettings</a> as an example. This is the class that controls most of the RDP Host settings on the computer.</p>

<p>Here is a snippet that will enable RDP on your local machine (run from an elevated shell):</p>

<pre><code class="language-powershell">$Win32TerminalServiceSettings = Get-CimInstance -Namespace root/cimv2/TerminalServices -ClassName Win32_TerminalServiceSetting 
$Win32TerminalServiceSettings | Invoke-CimMethod -MethodName SetAllowTSConnections -Arguments @{AllowTSConnections=1;ModifyFirewallException=1}
</code></pre>

<p>So lets break this down a bit.</p>

<pre><code class="language-powershell">$Win32TerminalServiceSettings = Get-CimInstance -Namespace root/cimv2/TerminalServices -ClassName Win32_TerminalServiceSetting
</code></pre>

<p>This line simply creates a variable containing the CIM Class object. This makes it simple to reuse if you want to invoke several methods, it can also help improve readability of your script.</p>

<pre><code class="language-powershell">$Win32TerminalServiceSettings | Invoke-CimMethod -MethodName SetAllowTSConnections -Arguments @{AllowTSConnections=1;ModifyFirewallException=1}
</code></pre>

<p>Here we are getting to the meat. We pipe the class object variable into the Invoke-CimMethod cmdlet. Here we first specify the method “SetAllowTSConnections” with the -MethodName parameter. Then we set our arguments with -Arguments, this parameter uses a hashtable to set the properties “AllowTSConnections” to 1 (allow) and “ModifyFirewallException” to 1 (enable firewall rule).</p>

<p>When you execute this command it will usually return a “0” to indicate success. You can check the setting by running the command:</p>

<pre><code class="language-powershell">Get-CimInstance -Namespace root/cimv2/TerminalServices -ClassName Win32_TerminalServiceSetting | select AllowTSConnections
</code></pre>

<p>It is important to note that calling the variable instead will return stale data, as the variable contains a snapshot of the parameters at the time of creation. This can be useful to compare data, but it is something to be aware of when you check for change.</p>

<h4 id="vs-wmi-object" id="vs-wmi-object">Vs WMI-Object</h4>

<p>For those that are more familiar with WMI this could be done with this command:</p>

<pre><code class="language-powershell">(Get-WmiObject -Namespace root/cimv2/TerminalServices -Class Win32_TerminalServiceSetting).SetAllowTsConnections(0,0)
</code></pre>

<p>This is an okay way to do it if your method does not have many parameters, but for some methods this can be really finicky.</p>

<p>The reason for this is that the parameter is positional in WMI, meaning that if you have several parameters but you only want to change one you will have to find it&#39;s correct position to change it. This is not always easy, as occasionally the parameter position is different that what the documentation says.</p>

<p>By using CIM methods you specify the name of the parameter in you arguments hashtable therefore rendering this issue moot.</p>

<h4 id="conclusion" id="conclusion">Conclusion</h4>

<p>As you can see above using CIM methods is fairly easy once you have the basics.</p>

<p>And while you can probably write shorter code with WMI, this can lead to issues when trying to set parameters.</p>

<p>My philosophy when writing scripts is that verbosity (within limits) is good, as it makes the script easier to read and understand and modify when necessary. And CIM cmdles are superior to WMI cmdlets in that way.</p>

<p>Also if you want to use the snippet above to enable RDP, you would probably also want to ensure that “Network Level Authenticaton” is enabled as well. You can do this with this snippet:</p>

<pre><code class="language-powershell">$Win32TSGeneralSettings = Get-CimInstance -Namespace root/cimv2/TerminalServices -ClassName Win32_TSGeneralSetting
$Win32TSGeneralSettings| Invoke-CimMethod -MethodName SetUserAuthenticationRequired -Arguments @{UserAuthenticationRequired=1}
</code></pre>

<p>Next article in the CIM series will talk about remote connections. Till then have a good one!</p>
]]></content:encoded>
      <guid>https://anitblog.no/powershell-working-with-cim-methods</guid>
      <pubDate>Fri, 06 Sep 2024 16:02:39 +0000</pubDate>
    </item>
    <item>
      <title>Resources</title>
      <link>https://anitblog.no/resources</link>
      <description>&lt;![CDATA[  Originally posted on 2018-06-15 on the old blog&#xA;&#xA;Here are a few various useful resources:&#xA;&#xA;Microsoft Docs - Documentation on all things Microsoft, should contain almost all the articles from the old MSDN TechNet as well.&#xA;&#xA;Stack Exchange - The root site for various excelent resources like Stack Overflow and Server Fault.&#xA;&#xA;RegExr - Excellent site for creating and testing Regular Expressions (RegEx) strings.&#xA;&#xA;SS64 - References for common scripting languages (across OS-es) and reference for database commands.&#xA;&#xA;My GitHub profile&#xA;]]&gt;</description>
      <content:encoded><![CDATA[<blockquote><p>Originally posted on 2018-06-15 on the old blog</p></blockquote>

<p>Here are a few various useful resources:</p>

<p><a href="https://docs.microsoft.com/">Microsoft Docs</a> - Documentation on all things Microsoft, should contain almost all the articles from the old MSDN TechNet as well.</p>

<p><a href="https://stackexchange.com/">Stack Exchange</a> - The root site for various excelent resources like <a href="https://stackoverflow.com/">Stack Overflow</a> and <a href="https://serverfault.com/">Server Fault</a>.</p>

<p><a href="https://regexr.com/">RegExr</a> - Excellent site for creating and testing Regular Expressions (RegEx) strings.</p>

<p><a href="https://ss64.com/">SS64</a> - References for common scripting languages (across OS-es) and reference for database commands.</p>

<p><a href="https://github.com/JMyklebust">My GitHub profile</a></p>
]]></content:encoded>
      <guid>https://anitblog.no/resources</guid>
      <pubDate>Fri, 06 Sep 2024 16:02:26 +0000</pubDate>
    </item>
    <item>
      <title>PowerShell - Working with CIM - Get information</title>
      <link>https://anitblog.no/powershell-working-with-cim-get-information</link>
      <description>&lt;![CDATA[  Originally posted on 2018-06-13 on the old blog&#xA;&#xA;This is the first part of how to work with CIM cmdlets; here i write about how to gather and sort information.&#xA;&#xA;Gathering information&#xA;&#xA;!--more--&#xA;&#xA;Gathering information with CIM is pretty straight forward. Using the class CIM\Process as an example.&#xA;&#xA;Get-CimInstance -ClassName CIMProcess&#xA;&#xA;This should list all running processes on your system.&#xA;&#xA;As with all PS objects you can also store you result in a variable and pipe this to your other commands:&#xA;&#xA;$Processes = Get-CimInstance -ClassName CIMProcess&#xA;&#xA;Keep in mind that this will be a snapshot of the moment you are creating the variable, so the data can become stale.&#xA;&#xA;Sorting and filtering&#xA;&#xA;Now you got some data to work with. But it might be somewhat unordered,  maybe you want to change the ordering of the list, or filter the results? That is easy to do!&#xA;&#xA;The CIM cmdlets like most other cmdlets returns data objects (instead of raw text), and PowerShell have a few different cmdlets for sorting and filtering the data.&#xA;&#xA;So for a few examples: Using the Sort-Object cmdlet we can sort the list according to the property we would like. Here we are sorting according to the &#34;name&#34; property:&#xA;&#xA;Get-CimInstance -ClassName CIMProcess | Sort-Object -Property Name&#xA;&#xA;Using the Where-Object cmdlet, we can filter for objects that has specific properties. Here we are filtering to only get the objects where the name begins with &#34;A&#34;&#xA;&#xA;Get-CimInstance -ClassName CIMProcess | Where-Object -Property Name -Like &#34;A&#34;&#xA;&#xA;Side note on using Where-Object:&#xA;There are a lot of ways to use this cmdlet, but going into that would be beyond the scope of this post.&#xA;In case you would like to read up on this, you can take a look at Microsoft&#39;s documentation of it; Where-Object&#xA;&#xA;Selecting properties&#xA;&#xA;By default the output might not contain all the data you want, but just is likely to show some of the most commonly relevant properties.&#xA;&#xA;Piping your command into Get-Member is an easy way to show all properties available to you:&#xA;&#xA;Get-CimInstance -ClassName CIMProcess | Get-Member&#xA;&#xA;Using the list of returned properties you can use the Select-Object cmdlet to get the properties you want:&#xA;&#xA;Get-CimInstance -ClassName CIMProcess | Select-Object -Property Name,ProcessID,CreationDate&#xA;&#xA;This should show you the process name, its ID, and when it started running.&#xA;&#xA;You can also see all properties for each object if you pipe your result to Format-List:&#xA;&#xA;Get-CimInstance -ClassName CIMProcess | Where-Object -Property Name -Like &#34;Notepad.exe&#34; | Format-List &#xA;&#xA;Note that I am also piping through Where-Object to avoid getting too many results (in this case looking for notepad.exe).&#xA;&#xA;It is important to note the difference between writing &#34;Format-List&#34; and &#34;Format-List \&#34;; adding the \ (wildcard) character will ensure you get all properties. In the odd case you are still not getting everything, appending -Force at the end  might help.&#xA;&#xA; &#xA;&#xA;Rounding off to keep this somewhat easy to read; this should be a decent beginning in understanding how to use CIM cmdlets. Any questions, corrections? Please comment below!&#xA;&#xA;The next article i will write about working with CIM methods. So till then, have a good one!&#xA;]]&gt;</description>
      <content:encoded><![CDATA[<blockquote><p>Originally posted on 2018-06-13 on the old blog</p></blockquote>

<p>This is the first part of how to work with CIM cmdlets; here i write about how to gather and sort information.</p>

<h3 id="gathering-information" id="gathering-information">Gathering information</h3>



<p>Gathering information with CIM is pretty straight forward. Using the class CIM_Process as an example.</p>

<pre><code class="language-powershell">Get-CimInstance -ClassName CIM_Process
</code></pre>

<p>This should list all running processes on your system.</p>

<p>As with all PS objects you can also store you result in a variable and pipe this to your other commands:</p>

<pre><code class="language-powershell">$Processes = Get-CimInstance -ClassName CIM_Process
</code></pre>

<p>Keep in mind that this will be a snapshot of the moment you are creating the variable, so the data can become stale.</p>

<p>Sorting and filtering</p>

<p>Now you got some data to work with. But it might be somewhat unordered,  maybe you want to change the ordering of the list, or filter the results? That is easy to do!</p>

<p>The CIM cmdlets like most other cmdlets returns data objects (instead of raw text), and PowerShell have a few different cmdlets for sorting and filtering the data.</p>

<p>So for a few examples: Using the Sort-Object cmdlet we can sort the list according to the property we would like. Here we are sorting according to the “name” property:</p>

<pre><code class="language-powershell">Get-CimInstance -ClassName CIM_Process | Sort-Object -Property Name
</code></pre>

<p>Using the Where-Object cmdlet, we can filter for objects that has specific properties. Here we are filtering to only get the objects where the name begins with “A”</p>

<pre><code class="language-powershell">Get-CimInstance -ClassName CIM_Process | Where-Object -Property Name -Like &#34;A*&#34;
</code></pre>

<pre><code>Side note on using Where-Object:
There are a lot of ways to use this cmdlet, but going into that would be beyond the scope of this post.
In case you would like to read up on this, you can take a look at Microsoft&#39;s documentation of it; Where-Object
</code></pre>

<h4 id="selecting-properties" id="selecting-properties">Selecting properties</h4>

<p>By default the output might not contain all the data you want, but just is likely to show some of the most commonly relevant properties.</p>

<p>Piping your command into Get-Member is an easy way to show all properties available to you:</p>

<pre><code class="language-powershell">Get-CimInstance -ClassName CIM_Process | Get-Member
</code></pre>

<p>Using the list of returned properties you can use the Select-Object cmdlet to get the properties you want:</p>

<pre><code class="language-markup">Get-CimInstance -ClassName CIM_Process | Select-Object -Property Name,ProcessID,CreationDate
</code></pre>

<p>This should show you the process name, its ID, and when it started running.</p>

<p>You can also see all properties for each object if you pipe your result to Format-List:</p>

<pre><code class="language-powershell">Get-CimInstance -ClassName CIM_Process | Where-Object -Property Name -Like &#34;Notepad.exe&#34; | Format-List *
</code></pre>

<pre><code>Note that I am also piping through Where-Object to avoid getting too many results (in this case looking for notepad.exe).
</code></pre>

<p>It is important to note the difference between writing “Format-List” and “Format-List *”; adding the * (wildcard) character will ensure you get all properties. In the odd case you are still not getting everything, appending -Force at the end  might help.</p>

<p> </p>

<p>Rounding off to keep this somewhat easy to read; this should be a decent beginning in understanding how to use CIM cmdlets. Any questions, corrections? Please comment below!</p>

<p>The next article i will write about working with CIM methods. So till then, have a good one!</p>
]]></content:encoded>
      <guid>https://anitblog.no/powershell-working-with-cim-get-information</guid>
      <pubDate>Fri, 06 Sep 2024 16:02:09 +0000</pubDate>
    </item>
    <item>
      <title>PowerShell - WMI and CIM</title>
      <link>https://anitblog.no/powershell-wmi-and-cim</link>
      <description>&lt;![CDATA[  Originally posted on 2018-05-17 on the old blog&#xA;&#xA;So for my fist post on this blog i&#39;ll write some musings about PowerShell and using the WMI and CIM cmdlets.&#xA;&#xA;WMI is the backbone of most PowerShell cmdlets that interact with system settings (and C# as well). And they are a powerful tool for collecting data and making system changes.!--more--&#xA;&#xA;WMI itself  is based upon the standard called Common Information Model (CIM) created by the Distributed Management Task Force (DMTF).&#xA;&#xA;Originally PowerShell v 1.0 was shipped with the following cmdlets for working with WMI:&#xA;&#xA;Invoke-WmiMethod&#xA;Register-WmiEvent&#xA;Get-WmiObject &#xA;Set-WmiInstance&#xA;Remove-WmiObject&#xA;&#xA;These are common workhorses for a lot of scripts around. But they are lacking a critical part of the PowerShell experience; discoverability.&#xA;&#xA;These cmdlets does not allow for autocomplete of classes and namespaces, meaning you would have to dig into the documentations to discover them.&#xA;&#xA;This was resolved with the release of PowerShell 3.0 and the CIM cmdlets:&#xA;&#xA;Get-CimAssociatedInstance&#x9;&#xA;Get-CimClass&#x9;&#xA;Get-CimInstance&#x9;&#xA;Get-CimSession&#x9;&#xA;Invoke-CimMethod&#x9;&#xA;New-CimInstance&#x9;&#xA;New-CimSession&#x9;&#xA;New-CimSessionOption&#x9;&#xA;Register-CimIndicationEvent&#x9;&#xA;Remove-CimInstance&#x9;&#xA;Remove-CimSession&#x9;&#xA;Set-CimInstance&#xA;&#xA;These cmdlets support autocomplete and makes it much easier to script without searching through documentation to find your classes and namespaces.&#xA;&#xA;Now writing about this in-depth would get really long, so i&#39;ll just link to Microsoft&#39;s documentation on these: WMI: https://msdn.microsoft.com/en-us/library/aa384642(v=vs.85).aspx.aspx &#34;https://msdn.microsoft.com/en-us/library/aa384642(v=vs.85).aspx&#34;) CIM\\MI: https://msdn.microsoft.com/en-us/library/jj819829(v=vs.85).aspx.aspx &#34;https://msdn.microsoft.com/en-us/library/jj819829(v=vs.85).aspx&#34;)&#xA;&#xA;Documentation on WMI classes included in Windows can be found here: https://msdn.microsoft.com/en-us/library/aa394554(v=vs.85).aspx.aspx)&#xA;&#xA;And Microsoft Docs is an excellent reference for PowerShell cmdlets (and everything else Microsoft): https://docs.microsoft.com/en-us/powershell/module/&#xA;&#xA;WMI vs CMI cmdlets&#xA;&#xA;I will focus on the CIM cmdlets, for a few reasons:&#xA;&#xA;Fist is auto-complete, which makes it much faster to create scripts in various editors (ISE primarily, but also VSCode), but also when you are just working in the console.&#xA;&#xA;Second reason is that CIM is much faster when trying to query multiple computers. This is because it will execute the commands in parallel, and also because it uses WSMan as the backend for remote management (same as PowerShell). One of the really nifty features of using WSMan as backend, is that you can create CIMSessions as well (more in a later post).&#xA;&#xA;And the third reason CIM is the way forward for PowerShell Core (PS v6.0), and will be supported on all platforms that PSCore is working on. Meaning you can reuse a lot of the knowledge from CIM on both Linux and MacOS as well.&#xA;&#xA;Here is a little snippet to show what i mean. This will use both WMI and CIM to get some information about the computers in the $ComputerList array. This will return hostname, Windows version and install date in a table. It will also do a measure with the same commands to show how much time each command takes to complete.&#xA;&#xA;$ComputerList = &#34;Computer1&#34;,&#34;Computer2&#34;,&#34;Computer3&#34;&#xA;&#xA;Get-CimInstance CIMOperatingSystem -ComputerName $ComputerList | Select CSName,Version,InstallDate&#xA;Get-WmiObject Win32OperatingSystem -ComputerName $ComputerList | Select CSName,Version,InstallDate &#xA;&#xA;Measure-Command {Get-CimInstance CIMOperatingSystem -ComputerName $ComputerList} &#xA;&#xA;Measure-Command {Get-WmiObject Win32OperatingSystem -ComputerName $ComputerList}&#xA;&#xA;Now querying 3 computers on the same network does not have a  really significant delay (both are likely below 1 second). But this quickly scales, and if one computer is slower than the others the Get-WMIObject will likely hang until it gets an response.&#xA;&#xA;The CIM cmdlet also shows another nice feature here. Date values will automatically be shown with the user&#39;s own time and date format (the PS object itself is unchanged). No need to do any additional step to understand the time and dates returned in CIM objects.&#xA;&#xA;This will be the first post about PowerShell and CIM in an ongoing series. So please check in later for more posts!&#xA;&#xA;Have a any feedback, suggestions, corrections and the like? Please do leave a comment!&#xA;&#xA;This is my first proper blog post (of hopefully many), and good feedback is the best way to improve!&#xA;]]&gt;</description>
      <content:encoded><![CDATA[<blockquote><p>Originally posted on 2018-05-17 on the old blog</p></blockquote>

<p>So for my fist post on this blog i&#39;ll write some musings about PowerShell and using the WMI and CIM cmdlets.</p>

<p>WMI is the backbone of most PowerShell cmdlets that interact with system settings (and C# as well). And they are a powerful tool for collecting data and making system changes.</p>

<p>WMI itself  is based upon the standard called Common Information Model (CIM) created by the Distributed Management Task Force (<a href="http://go.microsoft.com/fwlink/p/?linkid=67786">DMTF</a>).</p>

<p>Originally PowerShell v 1.0 was shipped with the following cmdlets for working with WMI:</p>

<pre><code class="language-powershell">Invoke-WmiMethod
Register-WmiEvent
Get-WmiObject 
Set-WmiInstance
Remove-WmiObject
</code></pre>

<p>These are common workhorses for a lot of scripts around. But they are lacking a critical part of the PowerShell experience; discoverability.</p>

<p>These cmdlets does not allow for autocomplete of classes and namespaces, meaning you would have to dig into the documentations to discover them.</p>

<p>This was resolved with the release of PowerShell 3.0 and the CIM cmdlets:</p>

<pre><code class="language-powershell">Get-CimAssociatedInstance	
Get-CimClass	
Get-CimInstance	
Get-CimSession	
Invoke-CimMethod	
New-CimInstance	
New-CimSession	
New-CimSessionOption	
Register-CimIndicationEvent	
Remove-CimInstance	
Remove-CimSession	
Set-CimInstance
</code></pre>

<p>These cmdlets support autocomplete and makes it much easier to script without searching through documentation to find your classes and namespaces.</p>

<p>Now writing about this in-depth would get really long, so i&#39;ll just link to Microsoft&#39;s documentation on these: WMI: https://msdn.microsoft.com/en-us/library/aa384642(v=vs.85).aspx.aspx”) CIM\MI: https://msdn.microsoft.com/en-us/library/jj819829(v=vs.85).aspx.aspx”)</p>

<p>Documentation on WMI classes included in Windows can be found here: <a href="https://msdn.microsoft.com/en-us/library/aa394554(v=vs.85).aspx">https://msdn.microsoft.com/en-us/library/aa394554(v=vs.85).aspx</a></p>

<p>And Microsoft Docs is an excellent reference for PowerShell cmdlets (and everything else Microsoft): <a href="https://docs.microsoft.com/en-us/powershell/module/">https://docs.microsoft.com/en-us/powershell/module/</a></p>

<h4 id="wmi-vs-cmi-cmdlets" id="wmi-vs-cmi-cmdlets">WMI vs CMI cmdlets</h4>

<p>I will focus on the CIM cmdlets, for a few reasons:</p>

<p>Fist is auto-complete, which makes it much faster to create scripts in various editors (ISE primarily, but also VSCode), but also when you are just working in the console.</p>

<p>Second reason is that CIM is much faster when trying to query multiple computers. This is because it will execute the commands in parallel, and also because it uses WSMan as the backend for remote management (same as PowerShell). One of the really nifty features of using WSMan as backend, is that you can create CIMSessions as well (more in a later post).</p>

<p>And the third reason CIM is the way forward for PowerShell Core (PS v6.0), and will be supported on all platforms that PSCore is working on. Meaning you can reuse a lot of the knowledge from CIM on both Linux and MacOS as well.</p>

<p>Here is a little snippet to show what i mean. This will use both WMI and CIM to get some information about the computers in the $ComputerList array. This will return hostname, Windows version and install date in a table. It will also do a measure with the same commands to show how much time each command takes to complete.</p>

<pre><code class="language-powershell">$ComputerList = &#34;Computer1&#34;,&#34;Computer2&#34;,&#34;Computer3&#34;

Get-CimInstance CIM_OperatingSystem -ComputerName $ComputerList | Select CSName,Version,InstallDate
Get-WmiObject Win32_OperatingSystem -ComputerName $ComputerList | Select CSName,Version,InstallDate 

Measure-Command {Get-CimInstance CIM_OperatingSystem -ComputerName $ComputerList} 

Measure-Command {Get-WmiObject Win32_OperatingSystem -ComputerName $ComputerList}
</code></pre>

<p>Now querying 3 computers on the same network does not have a  really significant delay (both are likely below 1 second). But this quickly scales, and if one computer is slower than the others the Get-WMIObject will likely hang until it gets an response.</p>

<p>The CIM cmdlet also shows another nice feature here. Date values will automatically be shown with the user&#39;s own time and date format (the PS object itself is unchanged). No need to do any additional step to understand the time and dates returned in CIM objects.</p>

<p>This will be the first post about PowerShell and CIM in an ongoing series. So please check in later for more posts!</p>

<p>Have a any feedback, suggestions, corrections and the like? Please do leave a comment!</p>

<p>This is my first proper blog post (of hopefully many), and good feedback is the best way to improve!</p>
]]></content:encoded>
      <guid>https://anitblog.no/powershell-wmi-and-cim</guid>
      <pubDate>Fri, 06 Sep 2024 16:01:58 +0000</pubDate>
    </item>
  </channel>
</rss>