title: How to determine if a string is an NT Account or a Security Identifier?
body: <h2>Question</h2>
  
  <p>How can I reliably tell if a given string represents an <code>NTAccount</code>, or a <code>SecurityIdentifier</code>?</p>
  
  <h2>Verbose</h2>
  
  <p>Given a string I can convert it to an <code>NTAccount</code> or a <code>SecurityIdentifier</code> via the constructor:</p>
  
  <pre><code>[string]$myAccount = '...' # some value
  $ntAccount = [System.Security.Principal.NTAccount]::new($myAccount)
  $sid = [System.Security.Principal.SecurityPrincipal]::new($myAccount) // throws exception if not a valid SID
  </code></pre>
  
  <p>If the string's not a SID, the SecurityIdentifier constructor will throw an exception.
  If the string's a SID, the NTAccount constructor will accept it... however, when I try to translate it to a SID, a <code>System.Security.Principal.IdentityNotMapped</code> exception will be thrown.</p>
  
  <pre><code>$sidFromNT = $ntAccount.Translate([System.Security.Principal.SecurityPrincipal]) # throw exception
  $ntFromSid = $sid.Translate([System.Security.Principal.NTAccount]) # should work as if the SID were invalid we'd have already erred
  </code></pre>
  
  <p>It would be great if I could say I don't know the type by using the shared base class; but that's abstract / has no public constructor; so I can't do this:</p>
  
  <pre><code>[string]$account = '...'
  $idRef = [System.Security.Principal.IdentityReference]::new($account) # this is not valid code
  $ntFromId = $idRef.Translate([System.Security.Principal.NTAccount])
  $sidFromId = $idRef.Translate([System.Security.Principal.SecurityIdentifier])
  </code></pre>
  
  <p>As such, the only options I can think of are variants of:</p>
  
  <ul>
  <li>Use the thrown error* to determine the type; but this makes the error part of my normal flow.</li>
  <li>Check if the value begins <code>S-</code>; very quick and should work in most cases; but it's a hack.</li>
  </ul>
  
  <p>* Note: I realize that an invalid value (i.e. neither an NT Account nor SID) would also through exceptions; I've ignored that scenario for now to remain brief.</p>
  
  <p>--</p>
  
  <h2>C# Version</h2>
  
  <p>Since this is a .net question rather than PowerShell specific, here's code to illustrate the same issue in C# (sadly I can't fiddle this, as the various Fiddle sites restrict the required functionality).</p>
  
  <pre><code>public static void Main()
  {
      var ids = new string[] {"S-1-1-0", "Everyone"}; // a list of values which may be SIDs or NTAccounts
      var pseudoRandomIndex = DateTime.Now.Millisecond % ids.Length; // saves initialising Random for a simple demo
      var idString = ids[pseudoRandomIndex]; // pick one id at random; be it a SID or an NT Account
  
      Debug.WriteLine($"Selected value is {idString}");
  
      TryToProcessIdentityReference&lt;NTAccount, SecurityIdentifier&gt;(idString, (id) =&gt; new NTAccount(id));
      TryToProcessIdentityReference&lt;SecurityIdentifier, NTAccount&gt;(idString, (id) =&gt; new SecurityIdentifier(id));
  }
  
  static void TryToProcessIdentityReference&lt;T1, T2&gt;(string idString, Func&lt;string, T1&gt; callConstructor)
      where T1 : IdentityReference
      where T2 : IdentityReference
  {
      var t1Type = typeof(T1);
      var t2Type = typeof(T2);
      Console.WriteLine($"Trying to process {idString} as a {t1Type.Name} ...");
      try 
      {
          var t1 = callConstructor(idString);
          _ = t1.Translate(t2Type); 
          Debug.WriteLine($" - {idString} is a valid {t1Type.Name}!");
      } catch (Exception e) when(e is ArgumentException || e is IdentityNotMappedException) { 
          Debug.WriteLine($" - Failed to process {idString} as {t1Type.Name}; error thrown when translating to {t2Type.Name}");
          Debug.WriteLine($" - {e.ToString()}");
      }   
  }
  </code></pre>