Advertisement
Guest User

Untitled

a guest
Feb 18th, 2020
481
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. #----------------------------------------------------------
  2. # STATIC / INITIAL VARIABLE DECLARATIONS
  3. #----------------------------------------------------------
  4. #
  5. $dateNow = Get-Date -Format yyyy-MM-dd
  6. $logFile = "path\logFile_$($dateNow).txt"
  7. $secretPath = "path\hmacKey.xml"
  8. $office365CredentialsPath = "path\office365Credentials.xml"
  9. $invalidRrnUserPath = "path\invalidRrnUsers_$($dateNow).csv"
  10. $script:errors = $false # used to send trigger error report for non-terminating errors
  11.  
  12. #----------------------------------------------------------
  13. # FUNCTION DEFINITIONS
  14. #----------------------------------------------------------
  15. #
  16. # add timestamp
  17. function Get-TimeStamp {
  18.     return Get-Date -f "yyyy-MM-dd HH:mm:ss -"
  19. }
  20.  
  21. # remove non-numerical characters from string
  22. Function Format-Num {
  23.     [System.Text.RegularExpressions.Regex]::Replace($args,"[^01-9]","");
  24. }
  25.  
  26. # test employeeNumber to see if value is empty, invalid, RRN or BIS
  27. Function Test-Rrn($employeeNumber){
  28.     If(!$employeeNumber){
  29.         Return $null
  30.     }
  31.     If($employeeNumber.Length -lt 11){
  32.         Return $false
  33.     }
  34.     #if user born after 2000 --> use different formula
  35.     If($employeeNumber[0] -eq '0'){
  36.         $employeeNumber_temp = '2' + $employeeNumber.SubString(0,9)
  37.         $validationNum = 97 - ([int64]$employeeNumber_temp % 97)
  38.  
  39.         If($validationNum -lt 10){$validationNum = "0$($validationNum)"}
  40.  
  41.         #check valid RRN
  42.         If(($employeeNumber.SubString(9,2) -eq [string]$validationNum) -and ([int]($employeeNumber.SubString(2,2) -le 12))){
  43.             Return "RRN"
  44.         }
  45.         #check valid BIS
  46.         ElseIf(($employeeNumber.SubString(9,2) -eq [string]$validationNum) -and (([int]($employeeNumber.SubString(2,2) -gt 20 -and [int]$employeeNumber.SubString(2,2) -le 52)))){
  47.             Return "BIS"
  48.         }
  49.         Else{
  50.             Return $false
  51.         }      
  52.     }
  53.     #if user is born before 2000, use default formula
  54.     Else{
  55.         $employeeNumber_temp = $employeeNumber.SubString(0,9)
  56.         $validationNum = 97 - ([int64]$employeeNumber_temp % 97)
  57.  
  58.         If($validationNum -lt 10){$validationNum = "0$($validationNum)"}
  59.  
  60.         #check valid RRN
  61.         If(($validationNum -eq $employeeNumber.SubString(9,2)) -and ([int]($employeeNumber.SubString(2,2) -le 12))){
  62.             Return "RRN"
  63.         }
  64.         #check valid BIS
  65.         ElseIf(($validationNum -eq $employeeNumber.SubString(9,2)) -and (([int]($employeeNumber.SubString(2,2) -gt 20 -and [int]$employeeNumber.SubString(2,2) -le 52)))){
  66.             Return "BIS"
  67.         }
  68.         Else{
  69.             Return $false
  70.         }
  71.     }
  72. }
  73.  
  74. # import hmac key
  75. function Import-HmacKey($secretPath){
  76.     try{
  77.         "$(Get-Timestamp) Importing CliXml containing hmac secret key." | Add-Content -Path $logFile
  78.         $secretSecurestring = (Import-CliXml -Path $secretPath -ErrorAction Stop).Password
  79.     }
  80.     catch [System.Security.Cryptography.CryptographicException]{
  81.         "$(Get-Timestamp) ERROR - DPAPI: Failed to decrypt key in current context. Make sure the script is running under the service account's context and from the original machine. Terminating script." | Add-Content -Path $logFile
  82.         $_.Exception.Message | Add-Content -Path $logFile
  83.         Send-ErrorReport
  84.         "$(Get-Timestamp) Script ended." | Add-Content -Path $logFile
  85.         exit
  86.     }
  87.     catch{
  88.         "$(Get-Timestamp) ERROR - Failed to import HMAC secret key. Please check logs for details. Terminating script." | Add-Content -Path $logFile
  89.         $_.Exception.Message | Add-Content -Path $logFile
  90.         Send-ErrorReport
  91.         "$(Get-Timestamp) Script ended." | Add-Content -Path $logFile
  92.         exit
  93.     }
  94.  
  95.     # convert hmac key from securestring to plaintext
  96.     $secretBstr = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($secretSecureString)
  97.     $secretHexString = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($secretBstr)
  98.  
  99.     return $secretHexString
  100. }
  101.  
  102. #import O365 credentials
  103. function Import-O365Credentials($office365CredentialsPath){
  104.     try{
  105.         "$(Get-Timestamp) Importing CliXml containing O365 credentials." | Add-Content -Path $logFile
  106.         $office365Credentials = Import-CliXml -Path $office365CredentialsPath -ErrorAction Stop
  107.         return $office365Credentials
  108.     }
  109.     catch [System.Security.Cryptography.CryptographicException]{
  110.         "$(Get-Timestamp) ERROR - DPAPI: Failed to decrypt key in current context. Make sure the script is running under the service account's context and from the original machine." | Add-Content -Path $logFile
  111.         $_.Exception.Message | Add-Content -Path $logFile
  112.         $script:errors = $true
  113.         return $false
  114.     }
  115.     catch{
  116.         "$(Get-Timestamp) ERROR - Failed to import O365 credentials. Please check logs for details." | Add-Content -Path $logFile
  117.         $_.Exception.Message | Add-Content -Path $logFile
  118.         $script:errors = $true
  119.         return $false
  120.     }
  121. }
  122.  
  123. # generate hmac signature
  124. function New-HmacSignature($secretHexString, $employeeNumber){
  125.     ### 1: Convert secret key hex string to byte array
  126.     # instantiate empty byte array
  127.     $secretByteArray = [Byte[]]::New($secretHexString.Length / 2)
  128.     # decode hex string and populate array
  129.     For($i=0; $i -lt $secretHexString.Length; $i+=2){
  130.         $secretByteArray[$i/2] = [System.Convert]::ToByte($secretHexString.SubString($i, 2), 16)
  131.         }
  132.  
  133.     ### 2: Generate hmac signature of rrn
  134.     # instantiate hmac object    
  135.     $hmacSha = New-Object System.Security.Cryptography.HMACSHA256
  136.     # set key to secret byte array
  137.     $hmacSha.key = $secretByteArray
  138.     # generate hmac signature byte array
  139.     $hmacSig = $hmacSha.ComputeHash([System.Text.Encoding]::ASCII.GetBytes($employeeNumber))
  140.  
  141.     ### 3: Convert hmac signature byte array to hex string
  142.     # instantiate string builder
  143.     $hexStringBuilder = [System.Text.StringBuilder]::New($hmacSig.Length * 2)
  144.     # generate hex string from hmac sig byte array
  145.     ForEach($byte in $hmacSig){
  146.         [void]$hexStringBuilder.AppendFormat("{0:x2}", $byte)
  147.         }
  148.     # cast builder to string
  149.     $hmacSigHexString = $hexStringBuilder.ToString()
  150.    
  151.     return $hmacSigHexString
  152. }
  153.  
  154. function New-UserList($secretHexString){
  155.     "$(Get-Timestamp) Creating collection of all user objects in default user OU." | Add-Content -Path $logFile
  156.  
  157.     $attempts = 0
  158.     $success = $false
  159.  
  160.     $params = @{
  161.         Filter = "*"
  162.         SearchBase = "OU=Users,DC=CONTOSO,DC=COM"
  163.         Properties = "enabled","name","sAMAccountName","userPrincipalName","employeeNumber","extensionAttribute15","whenCreated"
  164.     }
  165.     Do{
  166.         Try{
  167.             # list instead of array for performance
  168.             $allUsers = New-Object System.Collections.Generic.List[System.Object]
  169.             $user = Get-ADUser @params -ErrorAction Stop |
  170.                 Select-Object   @{Name="name";                  Expression={$_.name}},
  171.                                 @{Name="sAMAccountName";        Expression={$_.sAMAccountName}},
  172.                                 @{Name="userPrincipalName";     Expression={$_.userPrincipalName}},
  173.                                 @{Name="enabled";               Expression={$_.enabled}},
  174.                                 @{Name="employeeNumber";        Expression={Format-Num $_.employeeNumber}},
  175.                                 @{Name="rrnType";               Expression={Test-Rrn (Format-Num $_.employeeNumber)}},
  176.                                 @{Name="hmacSigAd";             Expression={$_.extensionAttribute15}},
  177.                                 @{Name="hmacSigCalc";           Expression={
  178.                                     switch ($_.employeeNumber){
  179.                                         {(Test-Rrn (Format-Num $_)) -eq "RRN"} {New-HmacSignature $secretHexString (Format-Num $_); break}
  180.                                         {(Test-Rrn (Format-Num $_)) -eq "BIS"} {New-HmacSignature $secretHexString (Format-Num $_); break}
  181.                                         default {$null}
  182.                                     }
  183.                                 }},
  184.                                 @{Name="whenCreated";           Expression={$_.whenCreated}}
  185.  
  186.             $allUsers.Add($user)
  187.             $allUsers = $allUsers[0]
  188.                                
  189.             "$(Get-Timestamp) Collected data on $($allUsers.Count) user objects." | Add-Content -Path $logFile
  190.             $success = $true
  191.         }
  192.         Catch{
  193.             "$(Get-Timestamp) Could not create collection of Kompas users. Trying again in five seconds." | Add-Content -Path $logFile
  194.             $_.Exception.Message | Add-Content -Path $logFile
  195.             $script:errors = $true
  196.             Start-Sleep -Seconds 5
  197.         }
  198.     }Until(($attempts -eq 5) -or $success)
  199.  
  200.     If(!$success){
  201.         "$(Get-Timestamp) ERROR - Failed to query AD for user data. The script will now send an error report and terminate." | Add-Content -Path $logFile
  202.         Send-ErrorReport
  203.         "$(Get-Timestamp) Script ended." | Add-Content -Path $logFile
  204.         exit
  205.     }
  206.    
  207.     $validRrnUsers = $allUsers | Where-Object {($_.rrnType -eq "RRN") -or ($_.rrnType -eq "BIS")}
  208.     $invalidRrnUsers = $allUsers | Where-Object {($_.rrnType -ne "RRN") -and ($_.rrnType -ne "BIS")}
  209.  
  210.     return $allUsers, $validRrnUsers, $invalidRrnUsers
  211. }
  212.  
  213. # sanity check; ensure correct data isn't overwritten in bulk because of hashing error
  214. # report and terminate if more than 50 changes would occur
  215. function Test-HmacSignatures($validRrnUsers){
  216.     $hmacDelta = $validRrnUsers | Where-Object {($_.hmacSigAd -ne $_.hmacSigCalc) -and ($_.hmacSigAd.Length -eq 64)}
  217.    
  218.     if($hmacDelta.Count -gt 50){
  219.         "$(Get-Timestamp) ERROR - Found $(($hmacDelta | Measure-Object).Count) mismatches in HMAC between AD and current calculated value. Root cause should be investigated." | Add-Content -Path $logFile
  220.         "$(Get-Timestamp) Terminating script without making any changes to AD." | Add-Content -Path $logFile
  221.         Send-ErrorReport
  222.         exit
  223.     }
  224.     "$(Get-Timestamp) Calculated $(($hmacDelta | Measure-Object).Count) HMAC signatures different to values stored in AD." | Add-Content -Path $logFile
  225.     "$(Get-Timestamp) Comparing calculated HMAC signature values to values stored in AD." | Add-Content -Path $logFile
  226. }
  227.  
  228. function Set-HmacSignature($user){
  229.     if(($user.hmacSigCalc -ne $user.hmacSigAd) -and ($user.hmacSigCalc.Length -eq 64)){
  230.         try{
  231.             Set-ADUser -Identity $user.sAMAccountName -Replace @{extensionAttribute15 = $user.hmacSigCalc} -ErrorAction Stop
  232.             "$(Get-Timestamp) Successfully set new HMAC signature for $($user.sAMAccountName) - $($user.hmacSigCalc)." | Add-Content -Path $logFile
  233.             "$(Get-Timestamp) Old value was: '$($user.hmacSigAd)'." | Add-Content -Path $logFile
  234.         }
  235.         catch{
  236.             "$(Get-Timestamp) ERROR - Failed to overwrite HMAC signature in AD for user $($user.sAMAccountName)." | Add-Content -Path $logFile
  237.             $_.Exception.Message | Add-Content -Path $logFile
  238.             $script:errors = $true
  239.         }
  240.     }
  241.     elseif($user.hmacSigCalc.Length -ne 64){
  242.         "$(Get-Timestamp) Calculated HMAC value is invalid: '$($user.hmacSigCalc)'." | Add-Content -Path $logFile
  243.         $script:errors = $true
  244.     }
  245.     else{
  246.         # calculated hmac sig matches hmac sig in ad - do nothing
  247.     }
  248. }
  249.  
  250. function Connect-Msol($office365Credentials){
  251.     $attempts = 0
  252.     $success = $false
  253.    
  254.     do{
  255.         try{
  256.             "$(Get-Timestamp) Connecting to MSOL." | Add-Content -Path $logFile
  257.             Connect-MsolService -Credential $office365Credentials -ErrorAction Stop
  258.             $success = $true
  259.         }
  260.         catch{
  261.             "$(Get-Timestamp) Failed to create connection to MSOL. Trying again in 5 seconds." | Add-Content -Path $logFile
  262.             $_.Exception.Message | Add-Content -Path $logFile
  263.             $script:errors = $true
  264.             Start-Sleep -Seconds 5
  265.         }
  266.         $attempts += 1
  267.     }until(($attempts -eq 5) -or $success)
  268.  
  269.     if(!$success){
  270.         "$(Get-Timestamp) ERROR - Failed to connect to MSOL to check user licences." | Add-Content -Path $logFile
  271.         "$(Get-Timestamp) Script will continue, but users with an invalid RRN will not be reported." | Add-Content -Path $logFile
  272.         return $false
  273.     }
  274.     return $true
  275. }
  276.  
  277. function Test-O365Licence($user){
  278.     try{
  279.         # ignore accounts without UPN
  280.         if($user.userPrincipalName.Length -gt 3){
  281.             $userObject = Get-MsolUser -UserPrincipalName $user.userPrincipalName -ErrorAction Stop
  282.         }
  283.     }
  284.     catch [Microsoft.Online.Administration.Automation.MicrosoftOnlineException]{
  285.         if($_.fullyQualifiedErrorId -eq "Microsoft.Online.Administration.Automation.UserNotFoundException,Microsoft.Online.Administration.Automation.GetUser"){
  286.             "$(Get-Timestamp) Not found in O365: $($user.userPrincipalName)." | Add-Content -Path $logFile
  287.         }
  288.         else{
  289.             "$(Get-Timestamp) ERROR - Error while searching for user: $($user.userPrincipalName)." | Add-Content -Path $logFile
  290.             $_.Exception.Message | Add-Content -Path $logFile
  291.         }
  292.     }
  293.     catch{
  294.         "$(Get-Timestamp) ERROR - Failed to find user $($user.userPrincipalName)." | Add-Content -Path $logFile
  295.         $_.Exception.Message | Add-Content -Path $logFile
  296.     }
  297.    
  298.     if($userObject.isLicensed -eq $true){
  299.         "$(Get-Timestamp) Has a licence in O365 but is not able to authenticate: $($user.userPrincipalName)." | Add-Content -Path $logFile
  300.         return $true
  301.     }
  302.     else{
  303.         #"$(Get-Timestamp) User $($user.userPrincipalName) has no O365 licence." | Add-Content -Path $logFile
  304.         return $false
  305.     }
  306. }
  307.  
  308. function Send-InvalidRrnUserReport{
  309.     $body = "
  310.    mail body
  311.    "
  312.  
  313.     $attempts = 0
  314.     $success = $false
  315.  
  316.     "$(Get-Timestamp) Sending report containing users who can't authenticate." | Add-Content -Path $logFile
  317.  
  318.     do{
  319.         try{
  320.             Send-MailMessage -SmtpServer "smtp.contoso.com" -To "" -From "" -Subject "mail title - $($dateNow)" -Body $body -Attachment $invalidRrnUserPath -ErrorAction Stop
  321.             "$(Get-Timestamp) Mail sent successfully." | Add-Content -Path $logFile
  322.             $success = $true
  323.         }
  324.         catch{
  325.             "$(Get-Timestamp) Error sending email, trying again in five seconds." | Add-Content -Path $logFile
  326.             $_.Exception.Message | Add-Content -Path $logFile
  327.             Start-Sleep -Seconds 5
  328.         }
  329.     }until(($attempts -eq 5) -or $success)
  330.  
  331.     if(!$success){
  332.         "$(Get-Timestamp) ERROR - Failed to send report on users with authentication problems." | Add-Content -Path $logFile
  333.         $script:errors = $true
  334.     }
  335. }
  336.  
  337. # send logfile in case nonterminating errors were logged during runtime
  338. function Send-ErrorReport{
  339.  
  340.     $body = "
  341.    mail body
  342.    "
  343.    
  344.     $attempts = 0
  345.     $success = $false
  346.  
  347.     "$(Get-Timestamp) Script encountered errors. Sending logfile for investigation." | Add-Content -Path $logFile
  348.  
  349.     do{
  350.         try{
  351.             Send-MailMessage -SmtpServer "smtp.contoso.com" -To "" -From "" -Subject "mail title - $($dateNow)" -Body $body -attachment $logFile -ErrorAction Stop
  352.             "$(Get-Timestamp) Mail sent successfully." | Add-Content -Path $logFile
  353.             $success = $true
  354.         }
  355.         catch{
  356.             "$(Get-Timestamp) Error sending email, trying again in five seconds." | Add-Content -Path $logFile
  357.             $_.Exception.Message | Add-Content -Path $logFile
  358.             Start-Sleep -Seconds 5
  359.         }
  360.     }until(($attempts -eq 5) -or $success)
  361.  
  362.     if(!$success){
  363.         "$(Get-Timestamp) Failed to send log file. Sending plaintext mail." | Add-Content -Path $logFile
  364.         $body = "
  365.        mail body
  366.        "
  367.  
  368.         Send-MailMessage -SmtpServer "smtp.contoso.com" -To "" -From "" -Subject "mail title - $($dateNow)" -Body $body
  369.     }
  370. }
  371.  
  372. #----------------------------------------------------------
  373. # FUNCTION CALLS
  374. #----------------------------------------------------------
  375. #
  376. "$(Get-Timestamp) Script started." | Add-Content -Path $logFile
  377.  
  378. $secretHexString = Import-HmacKey($secretPath)                                  # get secret from securestring
  379. $allUsers, $validRrnUsers, $invalidRrnUsers = New-UserList $secretHexString     # create list of users with calculated properties
  380. Test-HmacSignatures $validRrnUsers                                              # sanity check to ensure correct data isn't overwritten in bulk
  381. ForEach($user in $validRrnUsers){
  382.     Set-HmacSignature $user                                                     # set new hmac signature in ad if different
  383. }
  384. "$(Get-Timestamp) There are $(($invalidRrnUsers | Measure-Object).Count) users with an invalid RRN." | Add-Content -Path $logFile
  385.  
  386. $office365Credentials = Import-O365Credentials $office365CredentialsPath        # import service account credentials
  387. $msolConnectionResult = Connect-Msol $office365Credentials                      # connect to O365
  388. if($msolConnectionResult){              
  389.     $licencedUsers = @()
  390.     foreach($invalidRrnUser in $invalidRrnUsers){                               # for each user with an invalid RRN:
  391.         if($invalidRrnUser.whenCreated -gt (Get-Date).AddDays(-3)){
  392.             if(Test-O365Licence $invalidRrnUser){                               # test whether they exist in O365 and have an active licence
  393.                 $licencedUsers += $invalidRrnUser
  394.             }
  395.         }
  396.     }
  397.     if($licencedUsers.Count -gt 0){
  398.         "$(Get-Timestamp) There are $(($licencedUsers | Measure-Object).Count) users with an invalid RRN who do have an O365 licence." | Add-Content -Path $logFile
  399.         $licencedUsers | Sort-Object -Property sAMAccountName |
  400.         Select-Object name, sAMAccountName, userPrincipalName, enabled |
  401.             Export-Csv -Path $invalidRrnUserPath -NoTypeInformation -Encoding UTF8
  402.    
  403.         Send-InvalidRrnUserReport                                               # send mail report for licenced users with invalid RRN -> no auth possible
  404.     }
  405. }
  406.  
  407. if($script:errors){Send-ErrorReport}                                            # send mail report for nonterminating errors
  408. "$(Get-Timestamp) Script ended." | Add-Content -Path $logFile
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement