DansPSFuncs.psm1
<#
$path = "D:\_MyProfile\Documents\WindowsPowerShell\Modules\DansPSFuncs\DansPSFuncs.psd1" # $guid = [guid]::NewGuid().guid ## 43b2e1ad-93fc-4612-bfdf-625eba3af91a $paramHash = @{ Path = $path # AliasesToExport = "" Author = "Dan Murray" #RootModule = "DansPSFuncs.psm1" CompanyName = "Dan Murray" ModuleVersion = "1.1" Guid = $guid PowerShellVersion = "5.0" Description = "Dan Murrays functions" ReleaseNotes = "These are my custom functions" # FormatsToProcess = "" # FunctionsToExport = "" # VariablesToExport = "" # CmdletsToExport = "" } New-ModuleManifest @paramHash -passthru Publish-Module -Name DansPSFuncs -NuGetApiKey $NuGetApiKey #> <# Comment_based_Help .NOTES Author: DMurray == All my scripts are on Google Drive at: == https://drive.google.com/drive/folders/0BwvjuHnF0zm5RVN5bjhRNW5DSTA?usp=sharing == THIS script is located at: == https://drive.google.com/file/d/0BwvjuHnF0zm5RGczd0p4MVRxc1U/view?usp=sharing .SYNOPSIS Dans PowerShell Functions #> #Comment_based_Help Function __Process-HRBFiles { <# .SYNOPSIS Short description .DESCRIPTION Long description .EXAMPLE Example of how to use this cmdlet .EXAMPLE Another example of how to use this cmdlet .INPUTS Inputs to this cmdlet (if any) .OUTPUTS Output from this cmdlet (if any) .NOTES General notes .COMPONENT The component this cmdlet belongs to .ROLE The role this cmdlet belongs to .FUNCTIONALITY The functionality that best describes this cmdlet #> [CmdletBinding( SupportsShouldProcess=$true, PositionalBinding=$false )] Param ( <# Param1 help description [Parameter(Mandatory=$true, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, ValueFromRemainingArguments=$false, Position=0, ParameterSetName='Parameter Set 1')] [ValidateNotNull()] [ValidateNotNullOrEmpty()] [ValidateCount(0,5)] [ValidateSet("sun", "moon", "earth")] [Alias("p1")] $Param1, # Param2 help description [Parameter(ParameterSetName='Parameter Set 1')] [AllowNull()] [AllowEmptyCollection()] [AllowEmptyString()] [ValidateScript({$true})] [ValidateRange(0,5)] [int] $Param2, # Param3 help description [Parameter(ParameterSetName='Another Parameter Set')] [ValidatePattern("[a-z]*")] [ValidateLength(0,15)] [String] $Param3 #> #HRB Data Folder [Parameter( ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true )] $hrbDataDir='\\vab-vfp-01\hrb_updates$', #HRB Data FileSpec [Parameter( ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true )] $hrbDataFile = 'EMP LIST FOR ACTIVE DIRECTORY' ) Begin { #region BaseVariables #region MailSplats $smtpSplat = @{} $smtpSplat.from = 'McKean Help Desk <helpdesk@mckean-defense.com>' $smtpSplat.to = @('McKean Help Desk <helpdesk@mckean-defense.com>', 'John Scipione <JScipione@mckean-defense.com>', 'Dan Murray <dmurray@mckean-defense.com>') $smtpSplat.cc = @() $smtpSplat.SmtpServer = 'smtp.mckean.local' $smtpSplat.body = @() $RunMsgSplat = $smtpSplat $RunMsgSplat.Subject = "Started Process: $((Get-PSCallStack)[0].Command) at $((get-date).ToString("HH:mm MMM dd, yyyy"))" $RunMsgSplat.body = @() #LEGACY $mail = $smtpSplat $mail.from = 'McKean Help Desk <helpdesk@mckean-defense.com>' $Mail.to = @('McKean Help Desk <helpdesk@mckean-defense.com>', 'John Scipione <JScipione@mckean-defense.com>', 'Dan Murray <dmurray@mckean-defense.com>') $Mail.cc = @('Dan Murray <dmurray@mckean-defense.com>') $mail.SmtpServer = 'smtp.mckean.local' $mail.body = "See attached log file...", "#assign dmurray", "#close", "#mute" , "" | Out-String #endregion MailSplats $scriptName=(Get-PSCallStack)[0].Command $RunDate=(get-date).tostring("yyyyMMMdd-HHmm").ToUpper() $TheSrchBase = "OU=mckean",$((Get-ADDomain).distinguishedName) -join ',' $TermedBase = "OU=TERMED,OU=Special Users",$((Get-ADDomain).distinguishedName) -join ',' $SPGroupOU = 'OU=Sharepoint Groups,OU=Groups',$TheSrchBase -join ',' $ActvUsrOU = 'OU=Users',$TheSrchBase -join ',' $Summary = @() $TestEmplIDs = @(999,9999) $tab ="`t" $cr ="`r`n" #region Counts $Counts = @{} $Counts.i = 0 $Counts.NewUsers = 0 $Counts.DisUsers = 0 $Counts.NoAdds = 0 $Counts.Skipped = 0 $Counts.NotDisabled = 0 #endregion Counts #region ADP_2017_codes $ADP_2017_codes=@{} $ADP_2017_codes.add('1','') $ADP_2017_codes.add('101','EOM') $ADP_2017_codes.add('10103','CABRILLO GROUP') $ADP_2017_codes.add('010300','CAB - GRP MGMT') $ADP_2017_codes.add('010301','CAB - OPERATION') $ADP_2017_codes.add('10104','MCKEAN TECHNICAL SERVICES GROUP') $ADP_2017_codes.add('010400','MTS - GRP MGMT') $ADP_2017_codes.add('010401','MTS - PCO') $ADP_2017_codes.add('010402','MTS - READINESS, SUSTAINMENT, INDUSTRIAL OP') $ADP_2017_codes.add('010403','MTS - MODERNIZATION & TECHNOLOGY DEPLOYMENT') $ADP_2017_codes.add('010404','MTS - ADVANCED ENGINEERING & BUSINESS SOLUTIONS OP') $ADP_2017_codes.add('010405','MTS - ACQUISITION PROG & TRAINING SOLUTIONS OP') $ADP_2017_codes.add('010406','MTS - SURFACE STRATEGY & MAINTENANCE INTEGRATION OP') $ADP_2017_codes.add('010407','MTS - STRATEGIC SOLUTIONS CENTER') $ADP_2017_codes.add('10153','CABRILLO FS GROUP') $ADP_2017_codes.add('015300','CAB FS - GRP MGMT') $ADP_2017_codes.add('015301','CAB FS - OPERATION') $ADP_2017_codes.add('10154','MCKEAN FS TECHNICAL SERVICES GROUP') $ADP_2017_codes.add('015400','MTS FS - GRP MGMT') $ADP_2017_codes.add('015401','MTS FS - PCO') $ADP_2017_codes.add('015402','MTS FS - READINESS, SUSTAINMENT, INDUSTRIAL OP') $ADP_2017_codes.add('015403','MTS FS - MODERNIZATION & TECHNOLOGY DEPLOYMENT') $ADP_2017_codes.add('015404','MTS FS - ADVANCED ENGINEERING & BUSINESS SOLUTIONS OP') $ADP_2017_codes.add('015405','MTS FS - ACQUISITION PROG & TRAINING SOLUTIONS OP') $ADP_2017_codes.add('015406','MTS FS - SURFACE STRATEGY & MAINTENANCE INTEGRATION OP') $ADP_2017_codes.add('015407','MTS FS - STRATEGIC SOLUTIONS CENTER') $ADP_2017_codes.add('10190','CORP') $ADP_2017_codes.add('019000','CORP - OPERATIONS') $ADP_2017_codes.add('019001','CORP - HUMAN RESOURCES') $ADP_2017_codes.add('019002','CORP - MARKETING & BD') $ADP_2017_codes.add('019003','CORP - BUSINESS SERVICES') $ADP_2017_codes.add('019004','CORP - INFORMATION TECHNOLOGY') $ADP_2017_codes.add('019005','CORP - FINANCE') $ADP_2017_codes.add('019006','CORP - CONTRACTS') $ADP_2017_codes.add('10195','CORP FS') $ADP_2017_codes.add('019500','CORP FS - OPERATIONS') $ADP_2017_codes.add('019501','CORP FS - HUMAN RESOURCES') $ADP_2017_codes.add('019502','CORP FS - MARKETING & BD') $ADP_2017_codes.add('019503','CORP FS - BUSINESS SERVICES') $ADP_2017_codes.add('019504','CORP FS - INFORMATION TECHNOLOGY') $ADP_2017_codes.add('019505','CORP FS - FINANCE') $ADP_2017_codes.add('019506','CORP FS - CONTRACTS') $ADP_2017_codes.add('102','AG224') $ADP_2017_codes.add('10201','CABRILLO GROUP') $ADP_2017_codes.add('1020100','CAB - GRP MGMT') $ADP_2017_codes.add('1020101','CAB - OPERATION') $ADP_2017_codes.add('NEW','CABRILLO FS GROUP') try{$ADP_2017_codes.add('NEW','CAB FS - GRP MGMT')}catch{} #error cannot have a duplicate 'NEW' try{$ADP_2017_codes.add('NEW','CAB FS - OPERATION')}catch{} #error cannot have a duplicate 'NEW' #endregion ADP_2017_codes #endregion BaseVariables } # Begin Process { #region SelectTheCSV try { $TheCSV= Get-Filename ` -Title "Select CSV file to process" ` -Filter 'CSV Files (*.csv)|*.csv|Excel Files (*.xl*)|*.xlx*|All Files (*.*)|*.*' ` -initialDirectory $(split-path -parent $MyInvocation.MyCommand.Definition) } #try catch { Write-Host "Nothing selected!" -ForegroundColor Yellow } #catch #endregion SelectTheCSV #region Exit_If_Nothing_Selected if ($TheCSV -eq "") <# 20150801 Added code to abort processing if no file was selected #> { Write-host "Nothing to process, exiting script." -ForegroundColor Yellow rv * -ea 0 Write-Host "Goodbye." -ForegroundColor Yellow exit } else { $TheCSV = Get-Item $TheCSV #$RunLog = ($TheCSV.DirectoryName,'logs',($theCSV.basename,'run.log' -join '-') -join '\' $Logfile = $TheCSV.DirectoryName,'logs',($theCSV.basename,'run.log' -join '-') -join '\' } #endregion Exit_If_Nothing_Selected $UD=Import-Csv $TheCSV #region Exit-if-NOT-old-format if ($(Get-HRBformat -csvData $UD) -ne 'Old Format') { $msg = 'The selected CSV file is not in the proper format. Exiting.' Write-LogError -Message $msg -LogPath $Logfile -ToScreen -TimeStamp $mail.subject = "HRB Input: '$($thecsv.BaseName)' is not properly formatted." if (Test-Path $Logfile){$mail.attachments = $Logfile} $mail.body=$mail.body.ToString() Send-MailMessage @mail Write-Host "Goodbye." rv * -ea 0 exit } #endregion Exit-if-NOT-old-format #region MailBodyHeading $Mail.body += "=====================================" $Mail.body += "HRB Import Script v.$(Get-ScriptVersion)" $Mail.body += "=====================================" $Mail.body += "Begin Processing $($TheCSV.BaseName)" $Mail.body += "=====================================" $Mail.body | Out-String #endregion MailBodyHeading #region MainLoop foreach ($U in $UD){ #region common #region SkipTestUsers if ($TestEmplIDs -contains $u.'Employee Id') { $msg = "Skipping $u.'first name' $u.'Last Name' $u.'Employee Id'" Write-LogInfo -Message $msg -LogPath $Logfile -TimeStamp -ToScreen $MailBody = $MailBody,$msg -join $cr $Counts.Skipped++ continue } #endregion SkipTestUsers #endregion common #region Process Adds if ($u.tx -eq 'A' -and !(($TheCSV.Name -like 'term*'))) { #if ($u.tx -eq 'A' -and !(($TheCSV.Name -like 'term*'))) write-verbose " $(Get-Date -displayhint date) A" if ((get-aduser -filter "employeeID -like '$($U."Employee ID")'" -SearchBase $TheSrchBase -properties *) -eq $null) { $tmpSamAccountName = New-UniqueSAMName -lname $u.'Last Name' -FName $u.'First Name' -MI $u.'Middle Name' $NewUserName="$($U."Last Name"), $($U."First Name") " if ($($u."Middle Name".length -gt 0)) { $NewUserName=($NewUserName + " $($u."Middle Name".substring(0,1)).").trim() } #if ($($u."Middle Name".length -gt 0)) $msg = "New User, Empl #: $($u.employeeid), $($u.'First Name') $($u.'Last Name') -- Logon: $newUserName" Write-LogInfo -Message $msg -LogPath $Logfile -TimeStamp -ToScreen $NewUserPassword=("McKean-$($U."Employee ID")").trim() try # to create new user { #2012-12-21 -- Modified -path to ensure new users are created in the proper OU for auto mailbox creation Write-Host "Trying to create New User, Empl #: $($u.employeeid), $($u.'First Name') $($u.'Last Name') -- Logon: $newUserName" -ForegroundColor Green New-ADUser -SamAccountName $tmpSamAccountName -Name $newUserName -DisplayName $NewUserName ` -EmployeeID $($U."Employee ID").trim() -enabled $true -givenName $($U."First Name").trim() ` -surname $($U."Last Name") -path "ou=users,ou=mckean,dc=mckean,dc=local" ` -UserPrincipalName $tmpSamAccountName"@mckean.local" ` -AccountPassword (ConvertTo-SecureString -AsPlainText $NewUserPassword -Force) $Counts.NewUsers++ $i-=1 $msg = "Added EmplID: $($U."Employee ID") - $newUserName " Write-LogInfo -Message $msg -LogPath $Logfile -TimeStamp -ToScreen $MailBody = $($MailBody),$msg -join $cr } #try to create new user catch #failure when creating new user { $msg = "Unable to create user: $($NewUserName)" $msg = "NOT Added - EmplID: $($U."Employee ID").trim() - $($U."Last Name").trim(), $($U."First Name").trim [EmplID already exists]." Write-LogInfo -Message $msg -LogPath $Logfile -TimeStamp -ToScreen $u.tx="C" } #catch failure when creating new user } # if ((get-aduser -filter "employeeID -like '$($U."Employee ID")'" -SearchBase $TheSrchBase -properties *) -eq $null) else { #20120606:Added/modified various status lines # write-host "NOT Added - EmplID: $($U."Employee ID") - $($U."Last Name"), $($U."First Name") [EmplID already exists]" # -- 20120621.1 $msg= "NOT Added - EmplID: $($U."Employee ID") - $($U."Last Name"), $($U."First Name") [EmplID already exists]." Write-LogWarning -Message $msg -LogPath $Logfile -TimeStamp -ToScreen $MailBody = $($MailBody),$msg -join $cr $iNoAdds++ #20120606.1:Added feature to count NoAdds (already Present) } #else } #if ($u.tx -eq 'A' -and !(($TheCSV.Name -like 'term*'))) #endregion Process Adds #region Process Deletions if (($u.tx -eq 'D') -or ($TheCSV.Name -like 'terms*')) { $msg= "Disabling EmplID: $([string][int]$U.'Employee ID') - $($U.'Last Name'), $($U.'First Name')" Write-LogInfo -Message $msg -LogPath $Logfile -TimeStamp -ToScreen $MailBody = $MailBody,$msg -join $cr #region FindAllMatchingUsers $usrs2dis=@(get-aduser -filter "employeeID -like '$([string][int]$U.'Employee ID')*'" -properties *) #endregion FindAllMatchingUsers #region ProcessAllMatchingUsers if($usrs2dis) { #region Disable foreach ($usr in $usrs2dis) { if ($usr.enabled -eq $false) { $msg= "[Previously disabled: $($usr.description)]" $Counts.NotDisabled+=1 } # ($usr.enabled -eq $false) else { $msg= "Disabling '$($usr.samaccountname)'" try { $usr.Enabled=$FALSE Set-ADUser -Instance $usr Write-LogInfo -Message $msg -LogPath $Logfile -TimeStamp -ToScreen $MailBody = $MailBody,$msg -join $cr $Counts.DisUsers += 1} catch { Write-LogInfo -Message $msg -LogPath $Logfile -TimeStamp -ToScreen $MailBody = $MailBody,$msg -join $cr $msg= "Could not disable '$($usr.samaccountname)'" Write-LogError -Message $msg -LogPath $Logfile -TimeStamp -ToScreen $MailBody = $MailBody,$msg -join $cr} } # else } #foreach ($usr in $usrs2dis) #endregion Disable #region UpdateDescr try { $msg= "Updating disabled description" if($usr.Description -match "TERMED per HRB") { $msg= "Description already contained 'TERMED per HRB' " Write-LogInfo -Message $msg -LogPath $Logfile -TimeStamp -ToScreen $MailBody = $MailBody,$msg -join $cr } #if($usr.Description -match "TERMED per HRB") else { $usr.description="TERMED per HRB: $($TheCSV.BaseName)" Set-ADUser -Instance $usr Write-LogInfo -Message $msg -LogPath $Logfile -TimeStamp -ToScreen $MailBody = $MailBody,$msg -join $cr } #else } #try catch { $msg= "Could not update the description for: '$($usr.samaccountname)'" Write-LogError -Message $msg -LogPath $Logfile -TimeStamp -ToScreen $MailBody = $MailBody,$msg -join $cr } #catch #endregion UpdateDescr #region Expire try { $msg= "Updating Account Expiration to $(((Get-Date).date).AddDays(-7).ToString('yyyy-MMM-dd').ToUpper())" $usr.AccountExpirationDate=([datetime](Get-Date).date).AddDays(-7) Set-ADUser -Instance $usr Write-LogInfo -Message $msg -LogPath $Logfile -TimeStamp -ToScreen } catch { $msg= "Could not update the account expiration for: '$($usr.samaccountname)'" Write-LogError -Message $msg -LogPath $Logfile -TimeStamp -ToScreen } $MailBody = $MailBody,$msg -join $cr #endregion Expire #region NoMoreVPN try { if($usr.msNPAllowDialin -eq $FALSE) { $msg= " VPN Access already terminated." Write-LogInfo -Message $msg -LogPath $Logfile -TimeStamp -ToScreen $MailBody = $MailBody,$msg -join $cr } # if else { $msg= " Disabling VPN access for '$($usr.samaccountname)'" $usr.msNPAllowDialin=$false Set-ADUser -Instance $usr Write-LogInfo -Message $msg -LogPath $Logfile -TimeStamp -ToScreen } # else } # try catch { $msg= " Could not disable VPN access for: '$($usr.samaccountname)'" Write-LogError -Message $msg -LogPath $Logfile -TimeStamp -ToScreen } # catch $MailBody = $MailBody,$msg -join $cr #endregion NoMoreVPN #region RandomizePassword $RandomPass=[Web.Security.Membership]::GeneratePassword(32,16) $msg= " Randomizing passwoord for '$($usr.samaccountname)'" try { Set-ADAccountPassword -Identity $($usr.DistinguishedName) -Reset -NewPassword (ConvertTo-SecureString $($RandomPass) -AsPlainText -Force) Write-LogInfo -Message $msg -LogPath $Logfile -TimeStamp -ToScreen } # try catch { $msg= " Unable to randomize for '$($usr.samaccountname)'" Write-LogError -Message $msg -LogPath $Logfile -TimeStamp -ToScreen } # catch $MailBody = $MailBody,$msg -join $cr #endregion RandomizePassword #region MoveToTermed if ($usr.DistinguishedName -match $TermedBase) { $msg= " '$($usr.samaccountname)' is already in the 'Termed' OU" Write-LogInfo -Message $msg -LogPath $Logfile -TimeStamp -ToScreen } #if else { try { Move-ADObject -TargetPath $TermedBase -Identity $usr.DistinguishedName $msg= "'$($usr.samaccountname)' moved to $TermedBase" Write-Loginfo -Message $msg -LogPath $Logfile -TimeStamp -ToScreen $MailBody = $MailBody,$msg -join $cr } #try catch { $msg= "Failed to move '$($usr.samaccountname)' to $TermedBase" Write-LogError -Message $msg -LogPath $Logfile -TimeStamp -ToScreen $MailBody = $MailBody,$msg -join $cr } #catch } #else #endregion MoveToTermed } #endregion ProcessAllMatchingUsers #region NoMatchingUsers else { $msg= "Unable to locate EmplID: $([string][int]$U.'Employee ID') - $($U.'Last Name'), $($U.'First Name')" Write-LogError -Message $msg -LogPath $Logfile -TimeStamp -ToScreen $MailBody = $MailBody,$msg -join $cr } #else #endregion NoMatchingUsers rv usrs2dis } #endregion Process Deletions #region NoAction if ($u.tx -eq "-") { #No Action $msg="Skipped EmplID: $($U."Employee ID") - $($U."Last Name"), $($U."First Name")" Write-LogInfo -Message $msg -LogPath $Logfile -TimeStamp -ToScreen $MailBody = $MailBody,$msg -join $cr $Counts.Skipped++ } #endregion NoAction #region Process Changes/Updates if (!($U.TX) -or $u.tx -eq 'C' -or $u.TX -eq 'A' -and !(($TheCSV.Name -like 'term*'))) { $msg="Attempting to process EmplID: $($U."Employee ID") - $($U."Last Name"), $($U."First Name")" Write-LogInfo -Message $msg -LogPath $Logfile -TimeStamp -ToScreen #$usrs2upd=get-aduser -filter "employeeID -like '$($U.'Employee ID')*'" -SearchBase $TheSrchBase -properties * try { $usrs2upd=@(get-aduser -filter "employeeID -like '$([string][int]$U.'Employee ID')*'" -Properties *) $msg="Located $($usrs2upd.count) matching accounts" Write-LogInfo -Message $msg -LogPath $Logfile -TimeStamp -ToScreen } catch { $usrs2upd=@(get-aduser -filter "employeeID -eqlike '$([string][int]$u.'Associate ID')*'" -Properties *) $msg="Located $($usrs2upd.count) matching accounts" Write-LogInfo -Message $msg -LogPath $Logfile -TimeStamp -ToScreen } foreach($usr in $usrs2upd) { #region UpdStart $usr=get-aduser $usr.SamAccountName -Properties * $msg=" Updating user $($usr.name) [$($usr.samaccountname)]" Write-LogInfo -Message $msg -LogPath $Logfile -TimeStamp -ToScreen $MailBody = $MailBody,$msg -join '`r`n' #endregion UpdStart #region UpdName $newFirstName=$u.'First Name'.Replace(".","").replace("-","").replace(" ","").replace("'","").trim() $newLastName=$u.'Last Name'.Replace(".","").replace("-","").trim() if($u.'Middle Name' -ne '') { $newMI=$u.'Middle Name'.trim().substring(0,1) } $newCN="$newLastName, $newFirstName" #region UpdGivenName if($usr.GivenName -ne $newFirstName) { try { $usr.GivenName=$newFirstName Set-ADUser -Instance $usr $msg=" Updated first name to '$($newFirstName)'" Write-Loginfo -Message $msg -LogPath $Logfile -TimeStamp -ToScreen $MailBody = $MailBody,$msg -join '`r`n' } # try catch { $msg=" Failed to first name to '$($newFirstName)'" Write-LogError -Message $msg -LogPath $Logfile -TimeStamp -ToScreen $MailBody = $MailBody,$msg -join '`r`n' } #catch } #if else { $msg=" No Change in First Name" Write-Loginfo -Message $msg -LogPath $Logfile -TimeStamp -ToScreen $MailBody = $MailBody,$msg -join '`r`n' } #else #endregion UpdGivenName #region UpdSurName if($usr.Surname -ne $newLastName) { try { $usr.Surname = $newLastName Set-ADUser -Instance $usr $msg=" Updated last name to '$($newLastName)'" Write-Loginfo -Message $msg -LogPath $Logfile -TimeStamp -ToScreen $MailBody = $MailBody,$msg -join '`r`n' } #try catch { $msg=" Failed to update last name to '$($newLastName)'" Write-LogError -Message $msg -LogPath $Logfile -TimeStamp -ToScreen $MailBody = $MailBody,$msg -join '`r`n' } #catch } #if else { $msg=" No Change in Last Name" Write-Loginfo -Message $msg -LogPath $Logfile -TimeStamp -ToScreen $MailBody = $MailBody,$msg -join '`r`n' }#else #endregion UpdSurName #region UpdMI if ($($usr.initials) -ne $newMI) { try { $usr.Initials = $newMI Set-ADUser -Instance $usr $msg=" Updated middle initial to '$($newMI)'" Write-Loginfo -Message $msg -LogPath $Logfile -TimeStamp -ToScreen $MailBody = $MailBody,$msg -join '`r`n' } #try catch { $msg=" Failed to update middle initial to '$($newMI)'" Write-LogError -Message $msg -LogPath $Logfile -TimeStamp -ToScreen $MailBody = $MailBody,$msg -join '`r`n' } #catch } #if else { $msg=" No Change in Middle Initial" Write-Loginfo -Message $msg -LogPath $Logfile -TimeStamp -ToScreen $MailBody = $MailBody,$msg -join '`r`n' } #else #endregion UpdMI #region UpdCN if ($usr.cn -ne $newCN) {try {Rename-ADObject $usr.ObjectGUID -newname $newCN $usr=Get-ADUser $usr.ObjectGUID $msg=" Updated CN to $newCN" Write-LogInfo -Message $msg -LogPath $Logfile -TimeStamp -ToScreen $MailBody = $MailBody,$msg -join '`r`n' } catch {$msg=" Failed Updating CN to $newCN" Write-LogError -Message $msg -LogPath $Logfile -TimeStamp -ToScreen $MailBody = $MailBody,$msg -join '`r`n' } } else {$msg=" No change needed to CN '$($newCN)'" Write-Loginfo -Message $msg -LogPath $Logfile -TimeStamp -ToScreen $MailBody = $MailBody,$msg -join '`r`n' } #endregion UpdCN #endregion UpdName #region UpdEmail $properemailaddress=($($usr.SamAccountName) + $(if($usr.Department -like "cab*") {"@cabrillotechnologies.com"} else {"@mckean-defense.com"} ) ) if ($usr.mail -ne $properemailaddress) {try {$usr.mail = $properemailaddress Set-ADUser -Instance $usr $msg=" Updated email address to '$($properemailaddress)'" Write-Loginfo -Message $msg -LogPath $Logfile -TimeStamp -ToScreen $MailBody = $MailBody,$msg -join '`r`n' } catch {$msg=" Failed to update email address to '$($properemailaddress)'" Write-LogError -Message $msg -LogPath $Logfile -TimeStamp -ToScreen $MailBody = $MailBody,$msg -join '`r`n' } } else {$msg=" Email address appears correct" Write-Loginfo -Message $msg -LogPath $Logfile -TimeStamp -ToScreen $MailBody = $MailBody,$msg -join '`r`n' } #endregion UpdEmail #region UpdDialIn try {$usr.msNPAllowDialin = $true Set-ADUser -Instance $usr $msg=" Enabling Remote (VPN) Access" Write-LogInfo -Message $msg -LogPath $Logfile -TimeStamp -ToScreen $MailBody = $MailBody,$msg -join '`r`n'} catch {$msg=" Could not enable Remote (VPN) Access" Write-LogError -Message $msg -LogPath $Logfile -TimeStamp -ToScreen $MailBody = $MailBody,$msg -join '`r`n' } #endregion UpdDialIn #region UpdManager try {$uMgr=(get-aduser -filter "employeeID -eq '$([string][int]$U.'Manager ID')'" -Properties *)} catch {$uMgr=(get-aduser -filter "employeeID -eq '$([string][int]$u.'Reports To Associate ID')'" -Properties *) } if ($uMgr) { #region PWReset_Supervisor try{ $usr.EmployeeNumber = $uMgr.Surname.ToUpper() set-aduser -Instance $usr $msg=" Updated PWReset supervisor to $($uMgr.Surname.ToUpper())" Write-LogInfo -Message $msg -LogPath $Logfile -TimeStamp -ToScreen $MailBody = $MailBody,$msg -join '`r`n' } catch{ $msg=" Could not update PWReset supervisor" Write-LogError -Message $msg -LogPath $Logfile -TimeStamp -ToScreen $MailBody = $MailBody,$msg -join '`r`n' } #endregion PWReset_Supervisor #region AD_Manager try{ $usr.Manager = $uMgr.distinguishedname set-aduser -Instance $usr $msg=" Updated AD Manager to $($uMgr.name)" Write-LogInfo -Message $msg -LogPath $Logfile -TimeStamp -ToScreen $MailBody = $MailBody,$msg -join '`r`n' } catch{ $msg=" Could not update AD Manager" Write-LogError -Message $msg -LogPath $Logfile -TimeStamp -ToScreen $MailBody = $MailBody,$msg -join '`r`n' } #endregion AD_Manager } else {$msg=" Could not locate a manager with employee id: '$(if($u.'manager id'){[string][int]$u.'manager id'}else{[string][int]$u.'Reports To Associate ID'})'" Write-LogError -Message $msg -LogPath $Logfile -TimeStamp -ToScreen $MailBody = $MailBody,$msg -join '`r`n' } #endregion UpdManager #region UpdDeptName $newDeptName=if($u.'manager id'){$u.'Home Department'}else{$u.'Home Department Description'} if ($usr.department -ne $newDeptName) { try {$usr.department = $newDeptName set-aduser -Instance $usr $msg=" Updated Department to $($newDeptName)" Write-LogInfo -Message $msg -LogPath $Logfile -TimeStamp -ToScreen $MailBody = $MailBody,$msg -join '`r`n' } catch {$msg=" Could not update Department $($newDeptName)" Write-LogError -Message $msg -LogPath $Logfile -TimeStamp -ToScreen $MailBody = $MailBody,$msg -join '`r`n' } } #endregion UpdDeptName #region UpdCompany if ($U.'Employee ID'.substring(0,1) -eq '9') { $TheCoField = 'CRI'} else { $TheCoField='McKean Defense' } if ($usr.company -ne $TheCoField) {try { $usr.Company=$TheCoField Set-ADUser -Instance $usr $msg=" Updated Company to $($TheCoField)" Write-LogInfo -Message $msg -LogPath $Logfile -TimeStamp -ToScreen $MailBody = $MailBody,$msg -join '`r`n' } catch {$msg=" Could not update Department $($u.'Home Department')" Write-LogError -Message $msg -LogPath $Logfile -TimeStamp -ToScreen $MailBody = $MailBody,$msg -join '`r`n' } } else {$msg=" No Change in Company Name" Write-Loginfo -Message $msg -LogPath $Logfile -TimeStamp -ToScreen $MailBody = $MailBody,$msg -join '`r`n' } #endregion UpdCompany #region UpdOfficeLocation if ($usr.office -ne $U.location) {try {$usr.Office=$($u.'Location') Set-ADUser -Instance $usr $msg= " Updating Office to '$($U.location)'" Write-LogInfo -Message $msg -LogPath $Logfile -TimeStamp -ToScreen $MailBody = $MailBody,$msg -join '`r`n'} catch {$msg=" Could not update office location to $($u.'location')" Write-LogError -Message $msg -LogPath $Logfile -TimeStamp -ToScreen $MailBody = $MailBody,$msg -join '`r`n' } } else {$msg=" No Change in Office location" Write-Loginfo -Message $msg -LogPath $Logfile -TimeStamp -ToScreen $MailBody = $MailBody,$msg -join '`r`n' } #endregion UpdOfficeLocation #region UpdOfficePhone if (($u.'Work Phone'.length -ne 0 ) -and ($usr.OfficePhone -ne $($u.'Work Phone')) ) {try {$usr.OfficePhone=$($u.'Work Phone') Set-ADUser -Instance $usr $msg= " Updated OfficePhone to '$($U.'Work Phone')'" Write-LogInfo -Message $msg -LogPath $Logfile -TimeStamp -ToScreen $MailBody = $MailBody,$msg -join '`r`n'} catch {$msg=" Could not update OfficePhone to '$($U.'Work Phone')'" Write-LogError -Message $msg -LogPath $Logfile -TimeStamp -ToScreen $MailBody = $MailBody,$msg -join '`r`n' } } else {$msg=if($u.'Work Phone'.length -eq 0 ){" There is no OfficePhone listed"}else{" No Change in OfficePhone"} Write-LogInfo -Message $msg -LogPath $Logfile -TimeStamp -ToScreen $MailBody = $MailBody,$msg -join '`r`n' } #endregion UpdOfficePhone #region UpdDeptNo #$UD[1].'Home Department Code'.Replace('.','').padright(7,' ').substring(2,5) $dcode=if($u.'Home Department Code'.contains('.')) {[string][int]$U.'Home Department Code'.Replace('.','').padright(7,' ').substring(2,5)} else{[string][int]$U.'Home Department Code'} #$dcode if ($usr.departmentnumber -ne $dcode) { try { $usr.departmentnumber = $dcode Set-ADUser -Instance $usr $msg= " Updated Department ## to '$($dcode)'[$($u.'Home Department Code')]" Write-LogInfo -Message $msg -LogPath $Logfile -TimeStamp -ToScreen $MailBody = $MailBody,$msg -join '`r`n' } #try catch { $msg= " Failed Updating Department ## to '$($dcode)'[$($u.'Home Department Code')]" Write-LogError -Message $msg -LogPath $Logfile -TimeStamp -ToScreen $MailBody = $MailBody,$msg -join '`r`n' } #catch } #if ($usr.departmentnumber -ne $dcode) else { $msg=" No Change in Dept Number '$($dcode)'[$($u.'Home Department Code')]" Write-Loginfo -Message $msg -LogPath $Logfile -TimeStamp $MailBody = $MailBody,$msg -join '`r`n' } #else ($usr.departmentnumber -eq $dcode) #endregion UpdDeptNo #region UpdHomeZip $HomeZip=$(($u."Zip/Postal Code".PadLeft(5,"0")).substring(0,5)) if ($usr.carLicense -ne $HomeZip) {try {$usr.carLicense = $HomeZip Set-ADUser -Instance $usr $msg=" Updated Home zipcode to '$($HomeZip)'" Write-LogInfo -Message $msg -LogPath $Logfile -TimeStamp -ToScreen $MailBody = $MailBody,$msg -join '`r`n'} catch {$msg=" Could not update Home zipcode to '$($HomeZip)'" Write-LogError -Message $msg -LogPath $Logfile -TimeStamp -ToScreen $MailBody = $MailBody,$msg -join '`r`n' } } else {$msg=" No Change in Home zipcode '$($HomeZip)'" Write-Loginfo -Message $msg -LogPath $Logfile -TimeStamp -ToScreen $MailBody = $MailBody,$msg -join '`r`n' } rv HomeZip #endregion UpdHomeZip #region UpdEmplDOB #stored in 'employeetype' #$dob=([datetime]$u.'date of birth').tostring('dd') Write-Verbose $u.'Date of Birth' -Verbose $dob = &{ if ( $u.'Date of Birth'.Length -le 2 -and [int]$u.'Date of Birth' -ge 1 -and [int]$u.'Date of Birth' -le 31) {([int]$u.'Date of Birth').tostring('00')} else{([datetime]$u.'date of birth').tostring('dd')} } if ($usr.employeeType -ne $dob) { try {$usr.employeeType = $dob Set-ADUser -Instance $usr $msg=" Updating Date of birth" Write-LogInfo -Message $msg -LogPath $Logfile -TimeStamp -ToScreen $MailBody = $MailBody,$msg -join '`r`n'} catch {$msg=" Could not update Date of birth" Write-LogError -Message $msg -LogPath $Logfile -TimeStamp -ToScreen $MailBody = $MailBody,$msg -join '`r`n' } rv dob } #if ($usr.employeeType -ne $dob) #endregion UpdEmplDOB #region Description $desc="HRB: $($TheCSV.BaseName) $($RunTime)" try {$usr.Description = $desc Set-ADUser -Instance $usr $msg=" Updated Description" Write-LogInfo -Message $msg -LogPath $Logfile -TimeStamp -ToScreen $MailBody = $MailBody,$msg -join '`r`n'} catch {$msg=" Could not update Description" Write-LogError -Message $msg -LogPath $Logfile -TimeStamp -ToScreen $MailBody = $MailBody,$msg -join '`r`n' } rv desc #endregion Description $i++ } #foreach($usr in $usrs2upd) } #if (!($U.TX) -or $u.tx -eq 'C' -or $u.TX -eq 'A') #endregion Process Changes/Updates } #$U in $UD #endregion MainLoop #region Clean_up_AD_Dynamic_Department_Groups $GrpList=Get-ADGroup -filter {name -ne 'HRB-Managers' -and name -ne 'HRB-Executives' } -SearchBase $($SPGroupOU) -SearchScope Subtree -Properties * <# the following code block creates a summary of current group member counts { Get-ADGroup -filter {name -ne 'HRB-Managers' -and name -ne 'HRB-Executives' } -SearchBase $($SPGroupOU) -SearchScope Subtree -Properties * | ft @{name="Dept Code";expression={$_.samaccountname -replace 'HRB-DPT-', ''}}, @{name="Dept Name";expression={$_.cn -replace 'HRB-DEPT-', ''}}, @{name="Member Count";expression={(get-adgroupmember $_.samaccountname).name.count}} -AutoSize | out-file $Logfile -Encoding string -Append gc $logfile } i.e.: Dept Code Dept Name Member Count --------- --------- ------------ 1.01 EOM 0 1.01.01 RSS BU 0 1.01.01.00 RSS - OPS MGMT 0 1.01.01.01 RSS - MID ATLANTIC 0 1.01.01.02 RSS - PACIFIC 0 1.01.01.03 RSS - WATERFRONT SUPPT 0 1.01.01.04 RSS - NAVSEA 0 1.01.01.05 RSS - SURFMEPP 0 1.01.01.06 RSS - PROJ MGMT 0 1.01.02 STS BU 0 1.01.02.00 STS - OPS MGMT 0 1.01.02.01 STS - IT SERVICES 0 1.01.02.02 STS - MATERIALS & MACHINERY 2 1.01.02.03 STS - ENGINEERING SERVICES 0 1.01.02.04 STS - INTEGRATED FLEET SERVICES 2 1.01.02.05 STS - CRANE 0 1.01.02.06 STS - PROJ MGMT 0 1.01.03 Cabrillo BU 0 1.01.03.00 CAB - OPS MGMT 0 1.01.03.01 CAB - OPERATIONS 1 1.01.51 RSS FS BU 0 1.01.51.00 RSS FS - OPS MGMT 0 1.01.51.01 RSS FS - Mid Atlantic 0 1.01.51.02 RSS FS - PACIFIC 0 1.01.51.03 RSS FS - WATERFRONT SUPPT 0 1.01.51.04 RSS FS - NAVSEA 1 1.01.51.05 RSS FS - SURFMEPP 0 1.01.52 STS FS BU 0 1.01.52.00 STS FS - OPS MGMT 0 1.01.52.01 STS FS - IT Services 0 1.01.52.02 STS FS - Materials & Machinery 0 1.01.52.03 STS FS - Engineering Services 0 1.01.52.04 STS FS - Integrated Fleet Services 0 1.01.52.05 STS FS - CRANE 0 1.01.90 Corporate 0 1.01.90.00 CORP - OPERATIONS 0 1.01.90.01 CORP - HUMAN RESOURCES 1 1.01.90.02 CORP - MARKETING & BD 0 1.01.90.03 CORP - BUSINESS SERVICES 0 1.01.90.04 CORP - INFORMATION TECHNOLOGY 0 1.01.90.05 CORP - FINANCE 16 1.01.90.06 CORP - CONTRACTS 0 1.02 New Payroll Code 0 1.02.01 Cabrillo Technologies 0 1.02.01.00 Cabrillo - HQ 0 1.06 MCKEAN IT 0 #> #code block creates a summary of current group member counts $msg="Updating $($grpList.count) Dynamic Department Groups" Write-LogInfo -Message $msg -LogPath $Logfile -TimeStamp -ToScreen $MailBody = $MailBody,$msg -join $cr #region ProcessTheGroupList foreach ($Grp in $GrpList) { #region GetDeptNo #.SamAccountName.Replace('HRB-Dpt-','').replace('.','').padright(7,'0').substring(2,5) $DeptNo=$(($Grp.samaccountname).replace('HRB-Dpt-','').replace('.','').padright(7,'0').substring(2,5)) #endregion GetDeptNo #region get-initialMemberCount $initialMemberCount = (Get-ADGroupMember $Grp.distinguishedname).name.count #--- $initialMemberCount = $(if ($initialMemberCount -eq $null){"0"}else{$initialMemberCount}) #endregion get-initialMemberCount #region dePopulate-the-group #First, empty the group (http://stackoverflow.com/questions/26303857/powershell-remove-all-users-from-a-specific-group) Get-ADGroupMember $Grp.distinguishedname | ForEach-Object {Remove-ADGroupMember $Grp.distinguishedname $_ -Confirm:$false} #endregion dePopulate-the-group #region RE-populate-the-group #Select users from the McKean Users OU to the group that have the user.departmentNumber property equal to $DeptNo Get-ADUser -Filter { departmentNumber -eq $DeptNo } -SearchBase $ActvUsrOU -SearchScope Subtree | ForEach-Object {Add-ADGroupMember $Grp.distinguishedname $_.distinguishedname -Confirm:$false} #endregion RE-populate-the-group #region get-FinalMemberCount $finalMemberCount = (Get-ADGroupMember $Grp.distinguishedname).name.count #----$finalMemberCount = $(if ($finalMemberCount -eq $null){"0"}else{$finalMemberCount}) #endregion get-FinalMemberCount $msg="Group '$($Grp.name)' -- from $($initialMemberCount) -> $($finalMemberCount) members." Write-LogInfo -Message $msg -LogPath $Logfile -TimeStamp -ToScreen $MailBody = $MailBody,$msg -join $cr } #endregion ProcessTheGroupList #endregion Clean_up_AD_Dynamic_Department_Groups #region Check-for-users-without-descriptions Write-LogInfo -Message ' ' -LogPath $Logfile -ToScreen if ($NoDescrUsers){ $msg="We are still missing information for the following users:" Write-LogInfo -Message $msg -LogPath $Logfile -ToScreen foreach($ndu in $NoDescrUsers){ $msg= $ndu.employeeid,$ndu.name -join $tab Write-LogInfo -Message $msg -LogPath $Logfile -ToScreen} } else {$msg="All active AD users have descriptions." Write-LogInfo -Message $msg -LogPath $Logfile -TimeStamp -ToScreen $MailBody = $MailBody,$msg -join $cr } #endregion Check-for-users-without-descriptions #region AddSummaryToLogFile $sum=@" ===================================== $($thecsv.BaseName) Processing Complete! ===================================== Newly Disabled: $($Counts.DisUsers) Already Disabled: $($NotDisabled) New Adds: $($Counts.NewUsers) Already Added: $($iNoAdds) Updated: $($i) SKIPPED/ERRORS: $($Counts.Skipped) ===================================== Records Processed: $($ud.count) ===================================== "@ $sum|Out-File $Logfile -Append -encoding string #endregion AddSummaryToLogFile #region NotificationEmail $mail.subject = "$($TheCSV.BaseName) Processing Report".ToString() $mail.body = ('#assign dmurray','#mute','#close',$Sum)|out-string if (Test-Path $Logfile){$mail.attachments = $Logfile} Write-Host starting send Send-MailMessage @mail Write-Host complete! #endregion NotificationEmail #region MoveTheCSV try { $dest = ($TheCSV.DirectoryName,'completed',($RunDate,$TheCSV.BaseName -join ',') -join '\'),(get-item $TheCSV).Extension -join '' move-item -Path $TheCSV.FullName -Destination $dest rv dest } # try Catch { $msg="Unable to relocate the '$($TheCSV.basename)' file to '$($TheCSV.DirectoryName),'complete' -join '\')'" $mail.subject =$msg $mail.body = ('#assign dmurray','#mute','#close',$msg|Out-String) $mail.Remove('attachments') Send-MailMessage @mail } # Catch Finally{} #endregion MoveTheCSV } # Process End { $RunMsgSplat.body = ( $RunMsgSplat.body, " ===================================== ", " $($scriptName) Processing Complete! ", " ===================================== ", " Newly Disabled: $($Counts.DisUsers) ", " Already Disabled: $($NotDisabled) ", " New Adds: $($Counts.NewUsers) ", " Already Added: $($iNoAdds) ", " Updated: $($i) ", " SKIPPED/ERRORS: $($Counts.Skipped) ", " ===================================== ", " Records Processed: $($ud.count) ", " ===================================== " ) | Out-String Send-MailMessage @$RunMsgSplat } # End } #region MyLogfileName $MyLogfileName = { if (((Get-PSCallStack)[(Get-PSCallStack).count-1].Location -eq '<No file>')) { "Interactive", (get-date).tostring('yyyyMMddHHmmss'), 'log' -join '.' } else { ((Get-PSCallStack)[0].command, (get-date).tostring('yyyyMMddHHmmss'), 'log') -join '.' } } #endregion MyLogfileName #region RandomPasswordGeneration Add-Type -Assembly System.Web #endregion RandomPasswordGeneration Function Convert-HRBDepartmentNumber { param( [Parameter(Mandatory=$True, Position=0)] [string]$DepartmentNumber ) # param end (($DepartmentNumber.Replace('.','')) -replace '^10','').trim().PadRight(5,'0') } Function damChoice { [CmdletBinding( SupportsShouldProcess=$true )] # CmdletBinding Param ( # ChoiceMessage help description [Parameter( Mandatory=$true, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true )] [Alias("msg")] $ChoiceMessage <# # # Param2 help description # [Parameter(ParameterSetName='Parameter Set 1')] # [AllowNull()] # [AllowEmptyCollection()] # [AllowEmptyString()] # [ValidateScript({$true})] # [ValidateRange(0,5)] # [int] # $Param2, # # # Param3 help description # [Parameter(ParameterSetName='Another Parameter Set')] # [ValidatePattern("[a-z]*")] # [ValidateLength(0,15)] # [String] # $Param3 #> ) # Param Begin { } #Begin Process { Write-host ($ChoiceMessage,"?"," (Y/N) " -join '') -ForegroundColor Yellow -NoNewline $Readhost = Read-Host Switch ($ReadHost) { Y { Write-verbose $ChoiceMessage return $true} N { Write-verbose $ChoiceMessage return $false} Default { Write-Verbose "Default, Skip PublishSettings" return $false} } # Switch ($ReadHost) } # Process End { } # End } # Function damChoice Function Get-Clipboard([switch] $Lines) { if($Lines) { $cmd = { Add-Type -Assembly PresentationCore [Windows.Clipboard]::GetText() -replace "`r", '' -split "`n" } } else { $cmd = { Add-Type -Assembly PresentationCore [Windows.Clipboard]::GetText() } } if([threading.thread]::CurrentThread.GetApartmentState() -eq 'MTA') { & powershell -Sta -Command $cmd } else { & $cmd } } Function Get-Folder { [CmdletBinding()] param( [Parameter(Mandatory=$false, Position=0)] $InitialDirectory = $(if($PSCommandPath -eq ""){$env:userprofile}else{$((Get-Item $PSCommandPath).DirectoryName)}), [Parameter(Mandatory=$false, Position=1)] $Title = $null ) # param end [System.Reflection.Assembly]::LoadWithPartialName("System.windows.forms") | Out-Null $FolderBrowserDialog = New-Object System.Windows.Forms.FolderBrowserDialog $FolderBrowserDialog.SelectedPath = if ($FolderBrowserDialog.SelectedPath -eq "") {"\\vab-vfp-01\hrb_updates"} else {$FolderBrowserDialog.SelectedPath} # initial/default path $FolderBrowserDialog.ShowDialog() | Out-Null $FolderBrowserDialog.SelectedPath } #Function Get-Folder Function Get-FileName { <# .SYNOPSIS Creates a graphical OpenFileDialog box for file selection. .DESCRIPTION Generates a graphical OpenFileDialog box, returns the full pathname of a selected object, or $null if cancelled. .EXAMPLE Get-Filename Generates an openFileDialog window in the current folder. Displays all files and folders .EXAMPLE Get-Filename -filter 'All Files (*.*)|*.*' Generates an openFileDialog window in the current folder. Displays all files and folders .EXAMPLE Get-Filename -filter 'Powershell Files (*.ps*)|*.ps*' Generates an openFileDialog window in the current folder. Displays all powershell files. .EXAMPLE Get-Filename -filter 'Powershell Files (*.ps*)|*.ps*|Excel Files (*.xl*)|*.xlx*' Generates an openFileDialog window in the current folder. Displays all powershell files. Dialog includes a file selection filter dropdown with a second entry for Excel files. .PARAMETER initialDirectory The directory displayed when the dialog starts. The default is the current folder. .PARAMETER Filter Gets or sets a filter string that specifies the file types and descriptions to display in the OpenFileDialog. If the Filter property is Empty, all files are displayed. Folders are always displayed. Filter consists of a description of the filter followed by a vertical bar (|) and the filter pattern. The filter can specify one or more file types. The description describes the type of files shown in the dialog box. Although the description can be any string, it usually consists of the type of files included in the filter followed by the parentheses that contain the extensions associated with the description. The filter description appears in the drop-down list of the dialog box. The following is an example of a filter description. My Files (*.my) The filter pattern determines which files are shown by the dialog box. Filter patterns for the same description are separated by a semicolon (;). You can specify an exact match or use the wildcard character (*) in combination with the dot character (.) to specify file name or extension matches. The following is an example of a filter description followed by multiple filter patterns. This example adds Image Files (*.bmp, *.jpg) to the drop-down list and shows .bmp and .jpg files when it is selected. Image Files (*.bmp, *.jpg)|*.bmp;*.jpg Multiple filter options are separated by the vertical bar. The following is an example of multiple filter descriptions and patterns. This example adds Text Files (*.txt) and All Files (*.*) to the drop-down list. When Text Files (*.txt) is selected, .txt files are shown. When All Files (*.*) is selected, all file types are shown. Text Files (*.txt)|*.txt|All Files (*.*)|*.* ----------------------------- .PARAMETER Title Sets the default title of the dialog box. #> param( [Parameter(Mandatory=$false, Position=0, HelpMessage="Starting directory for Search")] $initialDirectory = $(if($PSCommandPath -eq ""){$env:userprofile}else{$((Get-Item $PSCommandPath).DirectoryName)}), [Parameter( HelpMessage="contains at least 2 pipe '|' separated parts i.e.:'CSV files (*.csv)| *.csv'" )] $Filter = "PowerShell (*.ps*)|*.ps*|CSV files (*.csv)| *.csv|ALL Files (*.*)| *.*", [Parameter(Mandatory=$false, Position=1)] $Title = $null ) # param end [System.Reflection.Assembly]::LoadWithPartialName("System.windows.forms") | Out-Null $OpenFileDialog = New-Object System.Windows.Forms.OpenFileDialog $OpenFileDialog.initialDirectory = $initialDirectory $OpenFileDialog.filter = $Filter $OpenFileDialog.Title=$Title $OpenFileDialog.ShowDialog() | Out-Null $OpenFileDialog.filename } #end function Get-FileName Function Get-HRBformat { <# .SYNOPSIS Determines HRB column format, New or Old .DESCRIPTION Determines HRB column format, New or Old .PARAMETER csvData Mandatory. data set created by the import-csv command .OUTPUTS None .NOTES Version: 1.0 Author: Dan Murray Creation Date: 11/29/2016 .LINK .EXAMPLE Get-HRBformat -csvData $importedCSV #> [CmdletBinding()] Param ( [Parameter(Mandatory=$true,Position=0)]$csvData ) $oldFields=( 'City', 'Date of Birth', 'Date of hire/rehire', 'Employee Id', 'Employee Status Type', 'First Name', 'Home Department', 'Home Department Code', 'Home email', 'Home phone', 'Job code', 'Job title', 'Last Name', 'Location', 'Manager ID', 'Middle Name', 'Mngr. FName', 'Mngr. LName', 'Mngr. MName', 'Original Date of hire', 'Status Eff. Date', # 'TX', 'Work Email', 'Work Phone', 'Zip/Postal Code') $oldFmt=$true $newFields=( 'Associate ID', 'Birth Date', 'Business Unit Description', 'File Number', 'First Name', 'Hire Date', 'Home Department Code', 'Home Department Description', 'Job Title Code', 'Job Title Description', 'Last Name', 'Location Description', 'Middle Name', 'Personal Contact: Home Phone', 'Personal Contact: Personal Email', 'Position Status', 'Primary Address: City', 'Primary Address: Zip / Postal Code', 'Rehire Date', 'Reports To Associate ID', 'Reports To First Name', 'Reports To Last Name', 'Reports To Middle Name', 'Status Effective Date', 'Work Contact: Work Email', 'Work Contact: Work Phone') $newFmt=$true $csvData| Get-Member -MemberType NoteProperty| Select-Object name| ForEach-Object -Begin {$csvFlds=@()} -Process {$csvFlds+=$_.name} Write-host "Checking Old Format" (0..([int]($oldfields.count)-1))|%{ if($oldFmt){ if($csvFlds -contains $oldfields[$_]) { } else { write-host "'$($oldFields[$_])' is missing" -ForegroundColor RED $oldFmt=$false } } } Write-host "Checking New Format" (0..([int]($newFields.count)-1))|%{ if($newFmt){ if($csvFlds -contains $newFields[$_]) { #write-host $newFields[$_], $newFmt -ForegroundColor Green } else { write-host "'$($newFields[$_])' is missing" -ForegroundColor RED $newFmt=$false } }} if($newFmt){'New Format'} elseif($oldFmt){'Old Format'} else{'Unknown Format'} } #end Function Get-HRBformat Function Get-MX ($domain) { try{(( Resolve-DnsName -Type MX $domain -ErrorAction Stop ) | where nameexchange -ne $null | sort preference ).nameexchange #((Resolve-DnsName -Type MX $domain) | where {$_.preference -eq 10}).NameExchange } catch {$null} } Function Get-ScriptOrCurrentPath { <# .NOTES Version: 3.0 Author: DMurray Creation Date: 2016JUN15 Purpose/Change: Initial script development $tsServer="http://timestamp.comodoca.com/authenticode" $mycert=(gci Cert:\CurrentUser\my -CodeSigningCert)[0] dir .\*.ps1 Set-AuthenticodeSignature (read-host "Script? ") -Certificate $mycert -TimestampServer $tsServer .SYNOPSIS Returns path of script or function file. If executed from the command line, it will return: '\\vab-vfp-01\hrb_updates$' .DESCRIPTION Returns path of script or function file. If executed from the command line, it will return: '\\vab-vfp-01\hrb_updates$' .PARAMETER Path Permits overriding default path #> #Comment_based_Help [CmdletBinding(SupportsShouldProcess=$true)] param( [Parameter(Mandatory=$false, Position=0)] [string]$Path = "\\vab-vfp-01\hrb_updates$" ) # param end Begin{} #Begin Process{ write-debug "in try" if ($PSScriptRoot -eq $null) {$Path} elseif ($PSScriptRoot -eq '') {$Path} elseif ($PSScriptRoot -match '\\Windows\\System32') {$Path} else {$PSScriptRoot} } #Process End{} #End } Function Get-ScriptVersion { <# .NOTES Version: 1.0 Author: DMurray Creation Date: 2016JUN15 Purpose/Change: Initial script development .SYNOPSIS Returns last modified date/time of script. If a script name is not provided, will return the current date/time as a string in the format 'yyyyMMMdd-HHmmss'" .DESCRIPTION Returns last modified date/time of script. If a script name is not provided, will return the current date/time as a string in the format 'yyyyMMMdd-HHmmss'" #> param( [Parameter( Mandatory=$false, HelpMessage="The full name and path of the script. If not provided, will return the current date/time" )] $ScriptPath ) if (!($ScriptPath)) {(get-date)} else {(Get-Item $ScriptPath).lastwritetime.tostring("yyyyMMMdd-HHmmss").ToUpper()} } Function MyCmdLets{ $Text = @" Function Verb-Noun { <# .SYNOPSIS A brief description of the function or script. This keyword can be used only once in each topic. .DESCRIPTION A detailed description of the function or script. This keyword can be used only once in each topic. .PARAMETER <Parameter-Name> The description of a parameter. Add a .PARAMETER keyword for each parameter in the function or script syntax. Type the parameter name on the same line as the .PARAMETER keyword. Type the parameter description on the lines following the .PARAMETER keyword. Windows PowerShell interprets all text between the .PARAMETER line and the next keyword or the end of the comment block as part of the parameter description. The description can include paragraph breaks. The Parameter keywords can appear in any order in the comment block, but the function or script syntax determines the order in which the parameters (and their descriptions) appear in help topic. To change the order, change the syntax. You can also specify a parameter description by placing a comment in the function or script syntax immediately before the parameter variable name. If you use both a syntax comment and a Parameter keyword, the description associated with the Parameter keyword is used, and the syntax comment is ignored. .EXAMPLE A sample command that uses the function or script, optionally followed by sample output and a description. Repeat this keyword for each example. .INPUTS The Microsoft .NET Framework types of objects that can be piped to the function or script. You can also include a description of the input objects. .OUTPUTS The .NET Framework type of the objects that the cmdlet returns. You can also include a description of the returned objects. .NOTES Additional information about the function or script. .LINK The name of a related topic. The value appears on the line below the .LINE keyword and must be preceded by a comment symbol (#) or included in the comment block. Repeat the .LINK keyword for each related topic. This content appears in the Related Links section of the help topic. The Link keyword content can also include a Uniform Resource Identifier (URI) to an online version of the same help topic. The online version opens when you use the Online parameter of Get-Help. The URI must begin with "http" or "https". .COMPONENT The technology or feature that the function or script uses, or to which it is related. This content appears when the Get-Help command includes the Component parameter of Get-Help. .ROLE The user role for the help topic. This content appears when the Get-Help command includes the Role parameter of Get-Help. .FUNCTIONALITY The intended use of the function. This content appears when the Get-Help command includes the Functionality parameter of Get-Help. .FORWARDHELPTARGETNAME <Command-Name> Redirects to the help topic for the specified command. You can redirect users to any help topic, including help topics for a function, script, cmdlet, or provider. .FORWARDHELPCATEGORY <Category> Specifies the help category of the item in ForwardHelpTargetName. Valid values are Alias, Cmdlet, HelpFile, Function, Provider, General, FAQ, Glossary, ScriptCommand, ExternalScript, Filter, or All. Use this keyword to avoid conflicts when there are commands with the same name. .REMOTEHELPRUNSPACE <PSSession-variable> Specifies a session that contains the help topic. Enter a variable that contains a PSSession. This keyword is used by the Export-PSSession cmdlet to find the help topics for the exported commands. .EXTERNALHELP <XML Help File> Specifies an XML-based help file for the script or function. The ExternalHelp keyword is required when a function or script is documented in XML files. Without this keyword, Get-Help cannot find the XML-based help file for the function or script. The ExternalHelp keyword takes precedence over other comment-based help keywords. If ExternalHelp is present, Get-Help does not display comment-based help, even if it cannot find a help topic that matches the value of the ExternalHelp keyword. If the function is exported by a module, set the value of the ExternalHelp keyword to a file name without a path. Get-Help looks for the specified file name in a language-specific subdirectory of the module directory. There are no requirements for the name of the XML-based help file for a function, but a best practice is to use the following format: <ScriptModule.psm1>-help.xml If the function is not included in a module, include a path to the XML-based help file. If the value includes a path and the path contains UI-culture-specific subdirectories, Get-Help searches the subdirectories recursively for an XML file with the name of the script or function in accordance with the language fallback standards established for Windows, just as it does in a module directory. For more information about the cmdlet help XML-based help file format, see "How to Create Cmdlet Help" in the MSDN (Microsoft Developer Network) library at http://go.microsoft.com/fwlink/?LinkID=123415. #> [CmdletBinding( DefaultParameterSetName='Parameter Set 1', SupportsShouldProcess=$true, PositionalBinding=$false, HelpUri = 'http://www.microsoft.com/', ConfirmImpact='Medium')] [Alias()] [OutputType([String])] Param ( <# # Param1 help description [Parameter( Mandatory=$true, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, ValueFromRemainingArguments=$false, Position=0, ParameterSetName='Parameter Set 1')] [ValidateNotNull()] [ValidateNotNullOrEmpty()] [ValidateCount(0,5)] [ValidateSet("sun", "moon", "earth")] [Alias("p1")] $Param1, # Param2 help description [Parameter( ParameterSetName='Parameter Set 1')] [AllowNull()] [AllowEmptyCollection()] [AllowEmptyString()] [ValidateScript({$true})] [ValidateRange(0,5)] [int] $Param2, # Param3 help description [Parameter( ParameterSetName='Another Parameter Set')] [ValidatePattern("[a-z]*")] [ValidateLength(0,15)] [String] $Param3 #> ) Begin { } #Begin Process { if ($pscmdlet.ShouldProcess("Target", "Operation")){ } # if ($pscmdlet.ShouldProcess } #Process End { } #End } "@ $Title = " DM Function Template" $Description = "My Tweak on Built-In 'Cmdlet (advanced function) - complete' template" $Author = "Dan Murray" if (-not(Get-IseSnippet | where name -Like $Title*)){ New-ISESnippet –Text $Text –Title $Title –Description $Description –Author $Author} else {write-host $Title -fore Green} } # Function MyCmdLets Function New-FileSystemWatcher { <# .DESCRIPTION Monitors changes to a folder. This uses a FileSystemWatcher object and its WaitForChanged method, which is rather nifty way to not use much resource to keep track of file system changes. What we’re NOT doing here is polling the folder contents. .LINK https://rcmtech.wordpress.com/2015/07/23/powershell-monitor-changes-to-a-folder/ #> [cmdletbinding(SupportsShouldProcess=$true)] Param ( [parameter()] [string]$Path, [parameter()] [ValidateSet('Changed','Created','Deleted','Renamed')] [string[]]$EventName, [parameter()] [string]$Filter, [parameter()] [System.IO.NotifyFilters]$NotifyFilter, [parameter()] [switch]$Recurse, [parameter()] [scriptblock]$Action ) #region Build FileSystemWatcher $FileSystemWatcher = New-Object System.IO.FileSystemWatcher If (-NOT $PSBoundParameters.ContainsKey('Path')){ $Path = $PWD } #If (-NOT $PSBoundParameters.ContainsKey('Path')) $FileSystemWatcher.Path = $Path If ($PSBoundParameters.ContainsKey('Filter')) { $FileSystemWatcher.Filter = $Filter } #If ($PSBoundParameters.ContainsKey('Filter')) If ($PSBoundParameters.ContainsKey('NotifyFilter')) { $FileSystemWatcher.NotifyFilter = $NotifyFilter } #If ($PSBoundParameters.ContainsKey('NotifyFilter')) If ($PSBoundParameters.ContainsKey('Recurse')) { $FileSystemWatcher.IncludeSubdirectories = $True } #If ($PSBoundParameters.ContainsKey('Recurse')) If (-NOT $PSBoundParameters.ContainsKey('EventName')){ $EventName = 'Changed','Created','Deleted','Renamed' } #If (-NOT $PSBoundParameters.ContainsKey('EventName')) If (-NOT $PSBoundParameters.ContainsKey('Action')){ $Action = { Switch ($Event.SourceEventArgs.ChangeType) { 'Renamed' { $Object = "{0} was {1} to {2} at {3}" -f $Event.SourceArgs[-1].OldFullPath, $Event.SourceEventArgs.ChangeType, $Event.SourceArgs[-1].FullPath, $Event.TimeGenerated } #'Renamed' Default { $Object = "{0} was {1} at {2}" -f $Event.SourceEventArgs.FullPath, $Event.SourceEventArgs.ChangeType, $Event.TimeGenerated } #Default } #Switch ($Event.SourceEventArgs.ChangeType) $WriteHostParams = @{ ForegroundColor = 'Green' BackgroundColor = 'Black' Object = $Object } #$WriteHostParams Write-Host @WriteHostParams } #$Action } #If (-NOT $PSBoundParameters.ContainsKey('Action')) #endregion Build FileSystemWatcher #region Initiate Jobs for FileSystemWatcher $ObjectEventParams = @{ InputObject = $FileSystemWatcher Action = $Action } #$ObjectEventParams ForEach ($Item in $EventName) { $ObjectEventParams.EventName = $Item $ObjectEventParams.SourceIdentifier = "File.$($Item)" Write-Verbose "Starting watcher for Event: $($Item)" $Null = Register-ObjectEvent @ObjectEventParams } #ForEach ($Item in $EventName) #endregion Initiate Jobs for FileSystemWatcher } Function New-LogFileName() { param( [Parameter( Mandatory=$false, HelpMessage="The Base Name of the log file. Needs to include the path. If running interactively, will return 'Console'" )] $BaseName, [Parameter( Mandatory=$false, HelpMessage="The suffix of the log file name. Default is 'run.log'" )] $Suffix='Run.log', [Parameter( Mandatory=$false, HelpMessage='Appends a timestamp to the base filename if $true.' )] $TimeStamp=$false ) if (!($BaseName)) { if(!($MyInvocation.MyCommand.Path)) {"$($env:TMP)`\Console",[string](get-random),$suffix -join '-'} else { if($TimeStamp) {(get-item($MyInvocation.MyCommand.Path)).BaseName,$((Get-date).toString("yyyMMMdd-HHmm").toupper()),[string](get-random),$suffix -join '-'} else {(get-item($MyInvocation.MyCommand.Path)).BaseName,[string](get-random),$suffix -join '-'} } } # if (!($BaseName)) else #else if (!($BaseName)) { if($TimeStamp) {$BaseName,$((Get-date).toString("yyyMMMdd-HHmm").toupper()),[string](get-random),$Suffix -join '-' } else {$BaseName,[string](get-random),$Suffix -join '-' } } #else if (!($BaseName)) } Function New-UniqueSAMName { <# .NOTES Version: 1.0 Author: DMurray Creation Date: 2016APR14 Purpose/Change: Initial script development .SYNOPSIS Generate a unique SAMAccountName .DESCRIPTION Generates a unique SAMAccountName Uses First Initial, Last Name. If not unique, add additional letters of First Name. If still not unique, insert Middle Initial (if one is present). Finally, if STILL not unique, use format FirstMLast## (where ## is a 2-digit incrementing sequence number) FLast? -> FiLast? -> FirLast? -> FirsLast? -> FirstLast? -> FirstMLast? -> FirstMLast## Empl# Legal Name SAMAccountName 1 Sam Jackson SJackson 2 Sam Jackson SaJackson 3 Sam Jackson SamJackson 4 Sam Jackson SamJackson01 5 Sam Jackson SamJackson02 6 Samuel Jackson SamuJackson .PARAMETER GivenName First Name .PARAMETER Surname Last Name .PARAMETER MI Middle Initial #> #Comment_based_Help [cmdletbinding(SupportsShouldProcess=$true)] Param( [parameter(Mandatory=$true, HelpMessage="New Users' Last Name. This item is MANDATORY.")] [Alias('Last','LastName','Last Name','SurName')] [String]$LName, [parameter(Mandatory=$true, HelpMessage="New Users' First Name. This item is MANDATORY.")] [Alias('First','FirstName','First Name','GivenName')] [String]$FName, [parameter(Mandatory=$false, HelpMessage="New Users' Middle Name or Initial. This item is OPTIONAL.")] [Alias('Middle','MiddleName','MName')] [String]$MI="" ) $bUnique=$false $LName=($LName|%{($_-split " ")|%{($_.substring(0,1).toupper()+$_.substring(1).tolower()).replace(" ","").replace(".","").replace(",","") } }) -join "" $fnLen=$FName.Length $lnLen=$LName.Length while (-not $bUnique){ #"In outer while" 1..$FName.Length| %{ #Write-host "$_. Testing " -NoNewline $uSAMName=$FName.Substring(0,$_)+$LName #Write-host $tstName [string[]]$tusrs=(get-aduser -filter {samaccountname -eq $uSAMName} -Properties *) if($tusrs.Count -eq 0) { rv tusrs $bunique=$true break} #if($tusrs.Count -eq 0) } #1..$GivenName.Length #test again; inserting Middle Initial $uSAMName=$FName+$($mi.Trim())+$LName [string[]]$tusrs=(get-aduser -filter {samaccountname -eq $uSAMName} -Properties *) if($tusrs.Count -eq 0) { rv tusrs $bunique=$true break} #if($tusrs.Count -eq 0) #Still Here? Start adding numbers 01-99 1..99|%{ [string[]]$tusrs=(get-aduser -filter {samaccountname -eq "$tname$($_.tostring('00'))"} -Properties *) if($tusrs.Count -eq 0) { $uSAMName="$uSAMName$($_.tostring('00'))" rv tusrs $bunique=$true break} #if($tusrs.Count -eq 0) } # 1..99 } #while (-not $bUnique) #($tstName|%{($_-split " ")|%{($_.substring(0,1).toupper()+$_.substring(1).tolower()).replace(" ","").replace(".","").replace(",","") } }) -join "" $uSAMName } Function Notify-ExpiringAccounts { [CmdletBinding(SupportsShouldProcess=$true)] Param ( $MaxPwdAge = (Get-ADDefaultDomainPasswordPolicy).MaxPasswordage.Days, $DaysToExpire = 14 ) Begin { Write-Verbose "Beginning..." #region SUPPORTING_FUNCTIONS Function writeHtmlFooter { [cmdletbinding(SupportsShouldProcess=$true)] param($fileName) Add-Content $fileName "</body>" Add-Content $fileName "</html>" } # Function to write the HTML Footer to the file #endregion SUPPORTING_FUNCTIONS #region VARIABLES $AccountNoExpire = 9223372036854775807 #Value when the account expiration date is blank $DomainDN = (Get-ADDomain).distinguishedName $LogFile = New-LogFileName $ScriptVersion = (Get-ScriptVersion).tostring("ddMMMyyyyHHmmss").toupper() $ExpiringUserBody = @() $ExpiringAccountBody = @() $dUsers= (get-aduser -Properties * -Filter { (enabled -eq $true) } -SearchBase "ou=users,ou=mckean,dc=mckean,dc=local" )| select *, @{l="Expires" ;E={ $MaxPwdAge - ((Get-Date) - $_.PasswordLastSet).days }}, @{L="LastSeen" ;e={((get-date)-$_.lastlogondate).days}}, #Days Since Last Logon @{L="ExpiresIn";E={ ($_.AccountExpirationDate - (get-date)).days }}, @{L="MgrEmail" ;e={((get-aduser (Get-ADUser $_.samaccountname -Properties *).manager -properties mail).mail)}}, @{L="MgrGiven" ;e={((get-aduser (Get-ADUser $_.samaccountname -Properties *).manager).givenName)}} Write-Verbose "Locating Expiring accounts..." $ExpiringAccounts = $dUsers | where { ($_.enabled -eq $true) -and ($_.accountexpires -ne $AccountNoExpire) -and ($_.accountexpires -ne 0) } | Where {$_.ExpiresIn -le $DaysToExpire } $ExpiringAccounts|sort expires -Descending|ft name,samaccountname,expiresIn,AccountExpirationDate,EmployeeNumber,MgrGiven,MgrEmail -AutoSize Write-Verbose "Locating accounts with passwords expiring in the next $MaxPwdAge days..." $ExpiringPasswords = $dUsers | Where-Object {$_.Expires -gt 0 -AND $_.Expires -le $DaysToExpire } $ExpiringPasswords|sort expires -Descending|ft name,samaccountname,expires,passwordlastset,EmployeeNumber,MgrEmail -AutoSize #endregion VARIABLES Write-Verbose "Domain 'MaximumPasswordAge' is $($MaxPwdAge) days." Write-Verbose "Found $($ExpiringPasswords.count) accounts with passwords expiring within $($DaysToExpire) days" Write-Verbose "Found $($ExpiringAccounts.count) accounts set to expire soon." #"McKean-alerts@mckean-defense.com" } # Begin Process { #region ExpiringPasswords foreach($Acct in $ExpiringPasswords) { $mailSplat = @{} $mailSplat.From = "McKean HelpDesk <helpdesk@mckean-defense.com>" $mailSplat.BCC = #"McKean HelpDesk <helpdesk@mckean-defense.com>", #"Dan Murray <dmurray@mckean-defense.com>", "Dan Murray <dmurray@murdan.net>" $mailSplat.SmtpServer = "smtp.mckean.local" $mailsplat.Cc = "McKean-alerts@mckean-defense.com" $mailSplat.to = #"Dan Murray <dmurray@murdan.net>" &{ if($Acct.expires -le 7){ ($Acct.mail,$Acct.MgrEmail)} else { ($Acct.mail)}} # $Acct.mail #> $mailSplat.subject = "Reminder: Your '$($Acct.UserPrincipalName)' password will expire in $($Acct.Expires) days." $mailSplat.body = ( $acct.givenName, "Your $($Acct.UserPrincipalName) password will expire in $($Acct.Expires) days. " , "===============================================", "This is an automated notification. DO NOT REPLY. ", "Should you have questions, please send a message, with contact information to: ", " helpdesk@mckean-defense.com. ", "===============================================", "", "If you are using a McKean issued Windows machine AND you are physically in the Crane, San Diego, Philadelphia, DC, or Va Beach McKean Office,you can update your password by pressing CTRL-ALT-DEL and choosing 'Change a Password'. ", "", "===============================================", "NOTE: When you update your password using the password reset website at https://reset.mckean-defense.com, your physical computer will remain unaware of the network password change until it reconnects to the McKean LAN at one of the sites listed above. Until that time, you will need to continue to log in to your local machine with your current password, but remember that mSync and eMail will be IMMEDIATELY aware of the change.", "===============================================", "NOTE: This will also updates the password used to access mSync, and Company eMail. ", "===============================================", "ALERT: Make certain you update any saved passwords, especially on mobile devices such as iPads, Droids, iPhones, etc. Failure to do so WILL lockout your account as these devices attempt to login with expired (invalid) passwords. ", " ", "NOTE: When Outlook prompts you for your new password, don't forget that your USERNAME ends with '@mckean.LOCAL', NOT '@mckean-defense.com' ", " ", "Script Version: $ScriptVersion " | out-string ) Send-MailMessage @mailSplat } #endregion ExpiringAccounts #region ExpiringAccounts foreach($Acct in $ExpiringAccounts) { $mailSplat = @{} $mailSplat.subject = "Reminder: The account for '$($Acct.name)' will expire on $($Acct.AccountExpirationDate.tostring("dd MMM yyyy"))." $mailSplat.From = "McKean HelpDesk <helpdesk@mckean-defense.com>" $mailSplat.BCC = "Dan Murray <dmurray@mckean-defense.com>"#, #"Dan Murray <dmurray@murdan.net>" $mailSplat.SmtpServer = "smtp.mckean.local" $mailSplat.to = $Acct.MgrEmail #"Dan Murray <dmurray@murdan.net>" $mailsplat.Cc = "McKean-alerts@mckean-defense.com" $mailSplat.body = ($acct.givenName, "===============================================", "This is an automated notification. DO NOT REPLY. ", "Should you have questions, please send a message, with contact information to: ", " helpdesk@mckean-defense.com. ", "===============================================", " ", "The account for '$($Acct.displayname)' expires in $($acct.ExpiresIn) days on $($Acct.AccountExpirationDate.tostring("dd MMM yyyy")).", " ", "If this account is still required, please open a trouble ticket requesting the expiration date be extended. ", " ", "===============================================", "REMEMBER: ", "Non-employee accounts can only be authorized by a McKean employee for a maximum of 90 days at a time.", "===============================================", " ", "Script Version: $ScriptVersion " | out-string) Send-MailMessage @mailSplat } #endregion ExpiringAccounts } # Process End { } # End } #Notify-ExpiringAccounts -Verbose Function Remove-WindowsOld { <# .SYNOPSIS Remove-WindowsOld .DESCRIPTION Removes the Windows.Old folder from upgraded machines. .NOTES Source Information: https://regularitguy.com/2013/12/16/how-to-delete-windows-old-from-windows-server-2012-r2/ .EXAMPLE Example of how to use this cmdlet .EXAMPLE Another example of how to use this cmdlet #> [cmdletbinding(SupportsShouldProcess=$true)] Param ( # # Param1 help description # [Parameter(Mandatory=$true, # ValueFromPipelineByPropertyName=$true, # Position=0)] # $Param1, # # # Param2 help description # [int] # $Param2 ) #Param Begin { $JunctionSource = 'https://download.sysinternals.com/files/Junction.zip' # requires Junction.exe from SysInternals #if its not on the system, go get it from MS. $junction = &{ begin{write-host "Looking for 'Junction.exe'"} Process {$j=Get-ChildItem c:\junction.exe -Recurse -ErrorAction SilentlyContinue -Verbose} end{write-host 'Done!' return $j} } if (!($junction)) { invoke-webrequest -uri $JunctionSource -OutFile $( $env:TEMP,(split-path $JunctionSource -Leaf) -join '\') $junction = $( $env:TEMP,(split-path $JunctionSource -Leaf) -join '\') Expand-Archive -LiteralPath (get-item $junction).FullName -DestinationPath (get-item $junction).Directory -Force $junction=Get-ChildItem ($env:TEMP,"junction.exe" -join '\') -Recurse -ErrorAction SilentlyContinue -Verbose } #if $junction = (Get-Item $junction).FullName #Junction List File $JunctionList = (Get-Item $junction).fullname -replace 'exe','txt' } # Begin Process { # Create a reference file that lists all the junction points and # symbolic links in use by opening up a command prompt, changing # into C:source and running #clear screen $myargs = @( "-nobanner", "-accepteula", "-s", "-q", "c:\windows.old") cmd /c $junction $myargs > $junctionList #gc $junctionList # open up PowerShell ISE administrator rights and run the following # to remove all symbolic links and junction points in c:windows.old #region RemoveAllSymbolicLinksAndJunctions foreach ($line in [System.IO.File]::ReadLines($junctionList)) { if ($line -match “^\\”) { $file = $line -replace “(: JUNCTION)|(: SYMBOLIC LINK)”,”” & $junction -d “$file” } } # foreach #endregion RemoveAllSymbolicLinksAndJunctions # take ownership, grant rights and delete windows.old to recover space if (test-path c:\windows.old) { takeown /F C:windows.old /R /D Y remove-item 'c:\windows.old' -Recurse -Force } } # Process End { } # End } # Function Remove-WindowsOld Function Send-PasswordExpirationNotices { [cmdletbinding(SupportsShouldProcess=$true)] Param ( [Parameter( #Mandatory=$true, ValueFromPipelineByPropertyName=$true)] [switch]$TestOnly ) begin { # begin $AccountNoExpire=9223372036854775807 $BodyFileName = $home,"$(get-random).html" -join '\' $EmailTo = "dmurray@mckean-defense.com" $EmailFrom = "noreply@mckean.local" $date = ( get-date ).ToString('MM/dd/yyyy') $EmailSubject = "Expiring Users " + $date $SMTPServer = "smtp.mckean.local" $ExpiredBody = {@" <!DOCTYPE html> <html> <head><style> body{font-family:courier;} </style></head><body> <p>The Active Directory account for $($u.name) was disabled because it expired on $(($u.AccountExpirationDate).tostring("MMM dd,yyyy")).<p> <p>If this individual still: <ul> <li>is ACTIVELY working for McKean, and </li> <li>requires access to McKean eMail, mSync and Network resources;</li> </ul> please reply to this ticket.</p> <p>If the individual is a contractor or otherwise not a regular, full-time employee of McKean Defense, the account lifetime is limited to 60 days.</p> <p>This account may be extended until $( (get-date).AddDays(90).tostring("MMM dd,yyyy") ).</p> <p>Contractor and non-McKean personnel access to McKean IT assets can only be granted in ninety (90) day increments.</p> <p>Failure to reply to this email will result in the account being disabled on <strong>$(($u.AccountExpirationDate).tostring("MMM dd,yyyy"))</strong></p> <p>Thanks for your attention to this matter, <strong>Network Operations<strong></p> </body></html> "@} $ExpiringBody = {@" <!DOCTYPE html> <html> <head><style> body{font-family:courier;} </style></head><body> <p>The Active Directory account for $($u.name) will be expiring in <strong>$($daysLeft)</strong> days [$(($u.AccountExpirationDate).tostring("MMM dd,yyyy"))].<p> <p>If this individual still: <ul> <li>is ACTIVELY working for McKean, and </li> <li>requires access to McKean eMail, mSync and Network resources;</li> </ul> please reply to this ticket.</p> <p>If the individual is a contractor or otherwise not a regular, full-time employee of McKean Defense, the account lifetime is limited to 90 days.</p> <p>This account may be extended until $( (get-date).AddDays(90).tostring("MMM dd,yyyy") ).</p> <p>Contractor and non-McKean personnel access to McKean IT assets can only be granted in ninety (90) day increments.</p> <p>Failure to reply to this email will result in the account being disabled on <strong>$(($u.AccountExpirationDate).tostring("MMM dd,yyyy"))</strong></p> <p>Thanks for your attention to this matter, <strong>Network Operations<strong></p> </body></html> "@} # Create output file and nullify display output New-Item -ItemType file $BodyFileName -Force > $null $ReportTitle = 'Test Report' #region ReportHeader Add-Content $BodyFileName '<html>' Add-Content $BodyFileName '<head>' Add-Content $BodyFileName "<meta http-equiv='Content-Type' content='text/html; charset=iso-8859-1'>" Add-Content $BodyFileName ('<title>',$ReportTitle,'</title>' -join '') Add-Content $BodyFileName '<style>' Add-Content $BodyFileName 'body{' Add-Content $BodyFileName 'font-family:courier;' Add-Content $BodyFileName '}' Add-Content $BodyFileName 'table, th, td {' Add-Content $BodyFileName ' border: 1px solid black;' Add-Content $BodyFileName ' border-collapse: collapse;' Add-Content $BodyFileName '}' Add-Content $BodyFileName 'th, td {' Add-Content $BodyFileName ' padding: 5px;' Add-Content $BodyFileName '}' Add-Content $BodyFileName '</head>' Add-Content $BodyFileName '<body>' #endregion ReportHeader } # begin process { $ExpiringUsers=(get-aduser -Properties * -Filter { (enabled -eq $true) -and (accountexpires -ne $AccountNoExpire) -and (accountexpires -ne 0) }) if (!($ExpiringUsers)) #Nothing to process { Add-Content $BodyFileName '<p style="font-size:160%">' Add-Content $BodyFileName '<b>Nothing to process</b>' Add-Content $BodyFileName '</p>' Add-Content $BodyFileName '<p style="font-size:160%;color:green;">' Add-Content $BodyFileName $date Add-Content $BodyFileName '</p>' } #Nothing to process else # process matching accounts { # process matching accounts #region TableSetup Add-Content $BodyFileName '<table>' Add-Content $BodyFileName '<tr>' Add-Content $BodyFileName '<th>Days Left</th>' Add-Content $BodyFileName '<th>User</th>' Add-Content $BodyFileName '<th>Account</th>' Add-Content $BodyFileName '<th>Manager</th>' Add-Content $BodyFileName '<th>Manager''s eMail</th>' Add-Content $BodyFileName '</tr>' #endregion TableSetup foreach ($u in $ExpiringUsers) { #foreach ($u in $ExpiringUsers) Add-Content $BodyFileName '<tr>' $Mgr = (get-aduser -Identity $u.Manager -Properties *) switch((($u.AccountExpirationDate - (get-date)).days)) { # switch((($u.AccountExpirationDate - (get-date)).days)) {$_ -le 0} #already Expired { $daysLeft = $_ Add-Content $BodyFileName ('<td style="color:red;"><b>',$daysLeft ,'</td>' -join '') Add-Content $BodyFileName ('<td style="color:red;"><b>',$u.name,'</td>' -join '') Add-Content $BodyFileName ('<td style="color:red;"><b>',$u.samaccountname,'</td>' -join '') Add-Content $BodyFileName ('<td style="color:red;"><b>',(get-aduser -Identity $u.manager -Properties *).name,'</td>' -join '') Add-Content $BodyFileName ('<td style="color:red;"><b>',(get-aduser -Identity $u.manager -Properties *).mail,'</td>' -join '') #DISABLE EXPIRED USER ACCOUNT $u.Enabled = $false set-aduser -Instance $u # notify manager $mailto = if($testOnly) {'Dan Murray <dmurray@mckean-defense.com>'} else { @("$($mgr.givenname) $($mgr.surname) <$($mgr.mail)>") } Write-Debug $testonly Write-Verbose $mailto Send-MailMessage ` -To $mailto -bcc 'Dan Murray <dmurray@mckean-defense.com>' ` -Subject ('Expiring Employee Account:',$u.samaccountname,'Disabled' -join ' ') ` -From $EmailFrom ` -SmtpServer $SMTPServer ` -bodyashtml ` -Body $(&$ExpiredBody) break } #switch {$_ -le 0} #already Expired {$_ -lt 14} #expiring w/in 14 days { $daysLeft = $_ Add-Content $BodyFileName ('<td>',$daysLeft ,'</td>' -join '') Add-Content $BodyFileName ('<td>',$u.name,'</td>' -join '') Add-Content $BodyFileName ('<td>',$u.samaccountname,'</td>' -join '') Add-Content $BodyFileName ('<td>',(get-aduser -Identity $u.manager -properties *).name,'</td>' -join '') Add-Content $BodyFileName ('<td>',(get-aduser -Identity $u.manager -properties *).mail,'</td>' -join '') ## notify manager and user $mailto = if($testOnly) {'Dan Murray <dmurray@mckean-defense.com>'} else { @("$($u.givenname) $($u.surname) <$($u.mail)>", "$($mgr.givenname) $($mgr.surname) <$($mgr.mail)>") } Write-Debug $testonly Write-Verbose $mailto Send-MailMessage ` -To $mailto -bcc 'Dan Murray <dmurray@mckean-defense.com>' ` -Subject ('Expiring Employee Account:',$u.samaccountname,'Disabled' -join ' ') ` -From $EmailFrom ` -SmtpServer $SMTPServer ` -bodyashtml ` -Body $(&$ExpiringBody) break } #switch {$_ -lt 14} #expiring w/in 14 days default #expiring, but not within 14 days, and not already expired { Add-Content $BodyFileName ('<td>',$_ ,'</td>' -join '') Add-Content $BodyFileName ('<td>',$u.name,'</td>' -join '') Add-Content $BodyFileName ('<td>',$u.samaccountname,'</td>' -join '') Add-Content $BodyFileName ('<td>',(get-aduser -Identity $u.manager -properties *).name,'</td>' -join '') Add-Content $BodyFileName ('<td>',(get-aduser -Identity $u.manager -properties *).mail,'</td>' -join '') break } #switch default #expiring, but not within 14 days, and not already expired } # switch((($u.AccountExpirationDate - (get-date)).days)) Add-Content $BodyFileName '</tr>' } #foreach ($u in $ExpiringUsers) Add-Content $BodyFileName '</table>' } # process matching accounts } # process end { #Clean up the HTML Add-Content $BodyFileName "</body>" Add-Content $BodyFileName "</html>" #Send summary Email # $BodyReport = Get-Content "$BodyFileName" -Raw Send-MailMessage ` -To $EmailTo ` -Subject $EmailSubject ` -From $EmailFrom ` -SmtpServer $SMTPServer ` -Body (Get-Content "$BodyFileName" -Raw) ` -BodyAsHtml (Get-Content "$BodyFileName" -Raw) } # end } #Function Send-PasswordExpirationNotices Function Sign-Me { [CmdletBinding( SupportsShouldProcess)] Param ( [Parameter(ValueFromPipeline=$true)] $file ) Begin { } Process { Write-Verbose 'process' $tsServer = "http://timestamp.comodoca.com/authenticode" $mycert = (gci Cert:\CurrentUser\my -CodeSigningCert) | Where {($_.NotAfter -ge (Get-Date))} if(!($file)) { Write-Verbose (Get-PSCallStack)[0].Location if ((Get-PSCallStack)[0].Location -eq '<no file>'){Write-Verbose (get-location)} $file = ( Get-FileName -initialDirectory $(get-location) ` -title "Select Script to Sign" ` -Filter 'All Files (*.*)|*.*|PowerShell Files (*.ps*)|*.ps*' ) } Write-Verbose (('', "Selected File: $file", " Certificate: $($mycert.FriendlyName)", " Valid until: $($mycert.NotAfter)", " Issued By: $($mycert.PSChildName)") | Out-String) try { if($mycert -and $file ){ Set-AuthenticodeSignature $file -Certificate $mycert -TimestampServer $tsServer } # if($mycert -and $file ) else { Write-Verbose 'No certificate or nothing to sign'} # else } #try catch { Write-Verbose 'Failed to Sign'} # catch } End { # Write-Verbose 'end'} } } # Function SignMe Function Test-ADCredentials { <# .NOTES Author: DMurray .SYNOPSIS Test AD Credentials .DESCRIPTION Tests AD Credentials .PARAMETER username username .PARAMETER password password .PARAMETER domain domain #> #Comment_based_Help Param($username, $password, $domain) Add-Type -AssemblyName System.DirectoryServices.AccountManagement $ct = [System.DirectoryServices.AccountManagement.ContextType]::Domain $pc = New-Object System.DirectoryServices.AccountManagement.PrincipalContext($ct, $domain) New-Object PSObject -Property @{ UserName = $username; IsValid = $pc.ValidateCredentials($username, $password).ToString() } } # Function Test-ADCredentials Function Test-Callstack { Function parent {child} # Function parent Function child { $callStack = Get-PSCallStack if ($callStack.Count -gt 1) { 'Parent' ' FunctionName: {0}' -f $callStack[1].FunctionName ' ScriptName: {0}' -f $callStack[1].ScriptName ' ScriptName Is Null: {0}' -f ($callStack[1].ScriptName -eq $null) if ($callStack[1].ScriptName -ne $null) { ' Script File: {0}' -f (get-item $callStack[1].ScriptName).name ' Script VersionInfo: {0}' -f (get-item $callStack[1].ScriptName).VersionInfo ' Script LastWriteTime: {0}' -f (get-item $callStack[1].ScriptName).LastWriteTime.ToString('yyyyMMdd.HHmmss') } ' Command: {0}' -f $callStack[1].Command ' Location Is Null? {0}' -f ($callStack[1].Location -eq $null) ' Location Is Blank? {0}' -f ($callStack[1].Location -eq '') ' Location: {0}' -f $callStack[1].Location 'Child' ' FunctionName: {0}' -f $callStack[0].FunctionName ' ScriptName: {0}' -f $callStack[0].ScriptName if ($callStack[1].ScriptName -ne $null) { ' Script File: {0}' -f (get-item $callStack[0].ScriptName).name ' Script VersionInfo: {0}' -f (get-item $callStack[0].ScriptName).VersionInfo ' Script LastWriteTime: {0}' -f (get-item $callStack[0].ScriptName).LastWriteTime.ToString('yyyyMMdd.HHmmss') } ' Command: {0}' -f $callStack[0].Command ' Location Is Null? {0}' -f ($callStack[0].Location -eq $null) ' Location Is Blank? {0}' -f ($callStack[0].Location -eq '') ' Location: {0}' -f $callStack[0].Location #$callStack[0]|gm } # Function child } '== Calling Parent ===============' parent '== Calling Child ===============' child } # Function Test-Callstack Function Test-NestedFunction { [cmdletbinding(SupportsShouldProcess=$true)] Param ( # Param1 help description $Param1, # Param2 help description $Param2 ) # Param Begin { Function InnerFunction1 { [cmdletbinding(SupportsShouldProcess=$true)] Param ( # Param1 help description $Param1, # Param2 help description $Param2 ) # Param Begin { Write-Verbose (Get-PSCallStack)[0].functionname } # Begin Process { Write-Verbose (Get-PSCallStack)[0].functionname } # Process End { Write-Verbose (Get-PSCallStack)[0].functionname } # End } # Function InnerFunction1 Function InnerFunction2 { [cmdletbinding(SupportsShouldProcess=$true)] Param ( # Param1 help description $Param1, # Param2 help description $Param2 ) # Param Begin { Write-Verbose (Get-PSCallStack)[0].functionname } # Begin Process { Write-Verbose (Get-PSCallStack)[0].functionname } # Process End { Write-Verbose (Get-PSCallStack)[0].functionname } # End } # Function InnerFunction2 $gpsc=(Get-PSCallStack)[0] $gpsc Write-Verbose $gpsc.functionname Write-Verbose (( ($gpsc.Location) -split ': ')[1]).trim() } # Begin Process { Write-Verbose (Get-PSCallStack)[0].functionname InnerFunction1 InnerFunction2 } # Process End { Write-Verbose (Get-PSCallStack)[0].functionname } # End } # Function Test-NestedFunction Function Update-McKeanManagersSharePointGroups { <# .NOTES Author: DMurray .SYNOPSIS Update McKean Managers SharePoint Groups .DESCRIPTION Update McKean Managers SharePoint Groups .PARAMETER LogFile Defaults to 'Generate-LogFileName' output .PARAMETER ScriptVer Defaults to 'Get-ScriptVersion' output .PARAMETER ShrPtGrpOU AD Organizational Unit containing SharePoint Groups. (ex: 'OU=Sharepoint Groups,OU=Groups,OU=McKean,dc=mckean,dc=local') .PARAMETER MgrList Manager List cdv file name (i.e.: 'mgrlist.csv' or 'McKean Managers.csv') .PARAMETER Subject SMTP Subject Line .PARAMETER SMTPServer FQDN of SMTP server .PARAMETER Sender Sending email addresse .PARAMETER Recipients Array of recipient email addresses #> #Comment_based_Help [cmdletbinding(SupportsShouldProcess=$true)] #needed to enable passthru of common paramaters like -verbose param( [string] $LogFile = $(New-LogFileName) , [string] $ScriptVer = $(if ($pscommandpath -eq '') { $((get-date).ToString("yyyyMMdd-HHmmss"))} else {(Get-Item $PSCommandPath).lastwritetime.tostring("yyyyMMdd-HHmmss")} ) , [string] $ShrPtGrpOU = "OU=Sharepoint Groups,OU=Groups,OU=McKean,dc=mckean,dc=local" , [string] $MgrList = $(get-filename -title "Select HRB .CSV Input File " -initialDirectory '\\vab-vfp-01\hrb_updates$' -Filter 'CSV Files (*.csv)|*.csv|All Files (*.*)|*.*') , [string] $Subject = "Sharepoint Managers Group Update" , [string] $SMTPServer = "smtp.mckean.local" , [string] $Sender = "$([environment]::UserName) <$([environment]::UserName)@$([environment]::UserDomainName).local>" , [string[]] $Recipients = @("Dan Murray <dmurray@mckean-defense.com>","HelpDesk <HelpDesk@mckean-defense.com>") ) # param end begin{ $mail = @{} $mail.SmtpServer = $SMTPServer $mail.from = $Sender $mail.subject = $Subject $mail.body = ('#assign dmurray','#mute','#close',$msg|Out-String) $mail.to = $Recipients if (Test-Path $Logfile){$mail.attachments = $Logfile} # Regexes # =================================================================== # Regex's used # [where 'Management Level' -match '[1-9A].*'] ==> All managers (and executives) # [where 'Management Level' -match '[1-9].*'] ==> All Executives # =================================================================== $RgX = @("'[1-9A].*'","'[1-9].*'") # Define each groups' name (samaaccountname), Description field, and Where string for selecting users from MgrList $ShrPtMgrsGrp = "HRB-Managers","McKean Managers","'Management Level' -match '[1-9A].*'" $ShrPtExecGrp = "HRB-Executives","McKean Executives","'Management Level' -match '[1-9].*'" # combine the group definitions for loop processing (scalability) $ShrPtGrps = $ShrPtMgrsGrp,$ShrPtExecGrp } # begin (vars and such) process{ try{ #Abort if no MgrList is supplied if ($MgrList.Length -eq 0 -or $MgrList -eq $null -or $MgrList -eq "" ) { Write-LogInfo -logpath $logfile -ToScreen -TimeStamp -Message "$((get-date).ToString("yyyyMMdd-HHmmss")) Nothing to process, exiting script." Write-LogInfo -logpath $logfile -ToScreen -TimeStamp -Message "$((get-date).ToString("yyyyMMdd-HHmmss")) Goodbye." throw "No Manager List supplied" } #if ($MgrList.Length -eq 0 -or $MgrList -eq $null -or $MgrList -eq "" ) else { $msg = "$($MgrList) Seems OK" Write-LogInfo -logpath $logfile -timestamp -ToScreen -message $msg } #else MgrList is supplied #region update the Executives $tname='HRB-Executives' Write-Verbose "update the Executives" #region check for existing group, create if not existing if (!(Get-ADGroup $tname)) { Write-LogInfo -LogPath $logfile -ToScreen -TimeStamp -Message "Creating group $tname" New-ADGroup $tname -Path $ShrPtGrpOU -Description $tdesc -GroupScope Global } #if (!(Get-ADGroup $tname)) #endregion check for existing group, create if not existing #region remove existing members Write-LogInfo -LogPath $logfile -TimeStamp -ToScreen -message "Cleaning group: $($tname)" (get-adgroupmember -identity $tname)| %{ Write-LogInfo -LogPath $logfile -TimeStamp -ToScreen -Message " - '$($_.name)'" remove-adgroupmember -identity $tname -members $_ -Confirm:$false } #endregion remove existing members #region (re) add members Write-LogInfo -logpath $logfile -timestamp -toscreen -message "Populating group: $($tname)" $newmembers=((import-csv -path $MgrList)|where 'Management Level' -match '[1-9].*') Write-Verbose "(re) add $($newmembers.count) Executives..." foreach($mbr in $newmembers) { #foreach($mbr in $newmembers) if ($mbr.'employee id') {$t_id=[string][int]$mbr.'employee id'} # if else {$t_id=[string][int]$mbr.'File Number'} # else $t_usr=(Get-ADUser -Filter "employeeid -like '$($t_id)'") $msg=" Adding $($t_usr.name)" Write-LogInfo -logpath $logfile -timestamp -ToScreen -message $msg Add-ADGroupMember -identity $tname -members $($t_usr.SamAccountName) -confirm:$false -ea 0 } #foreach($mbr in $newmembers) #endregion (re) add members #endregion update the Executives #region update the Managers $tname='HRB-Managers' Write-Verbose "Processing $tname" -Verbose #region check for existing group, create if not existing if (!(Get-ADGroup $tname)) { try { $msg="Creating group '$tname'" New-ADGroup $tname -Path $ShrPtGrpOU -Description $tdesc -GroupScope Global Write-LogInfo -logpath $logfile -timestamp -ToScreen -message $msg } #try Catch{ $msg="Unable to create group '$tname'" Write-LogError -logpath $logfile -timestamp -ToScreen -message $msg } #Catch } #if (!(Get-ADGroup $tname)) else {Write-Verbose " No need to create $tname" -Verbose} #endregion check for existing group, create if not existing #region remove existing members Write-Verbose " Remove existing members" -Verbose Write-LogInfo -logpath $logfile -TimeStamp -ToScreen -message "Cleaning group: $($tname)" (get-adgroupmember -identity $tname)|%{ try{ remove-adgroupmember -identity $tname -members $_ -Confirm:$false Write-LogInfo -logpath $logfile -timestamp -ToScreen -message " - '$($_.name)'" }#try{} catch{ Write-LogError -logpath $logfile -timestamp -ToScreen -message " Unable to remove '$($_.name)'" }#catch{} } #(get-adgroupmember -identity $tname)|%{ #endregion remove existing members #region (re) add members Write-LogInfo -logpath $logfile -timestamp -ToScreen -message "Populating $($tname)" $newmembers=((import-csv -path $MgrList)|where 'Management Level' -match '[1-9A].*') Write-Verbose "(re) add $($newmembers.count) Managers..." foreach($mbr in $newmembers) { #foreach($mbr in $newmembers) if ($mbr.'employee id') {$t_id=[string][int]$mbr.'employee id'} #if else {$t_id=[string][int]$mbr.'File Number'} #else $t_usr=(Get-ADUser -Filter "employeeid -like '$($t_id)'") $msg=" Adding $($t_usr.name)" Write-LogInfo -LogPath $logfile -TimeStamp -ToScreen -message $msg Add-ADGroupMember -identity $tname -members $($t_usr.SamAccountName) -confirm:$false -ea 0 } #foreach($mbr in $newmembers) #endregion (re) add members #endregion update the Managers #region Relocate the datafile try { $dest="$(split-path $MgrList)\completed\$((Get-Item $MgrList).basename)-cmplt-$((get-date).ToString("yyyyMMMdd-HHmm").ToUpper())$((Get-Item $MgrList).extension)";$dest move-item $MgrList -Destination $dest } #try Catch{ $msg = "Could not relocate $mgrList",$Error[0].Exception.Message -join "; " Write-LogError -LogPath $logfile -TimeStamp -ToScreen -Message $msg } #Catch #endregion Relocate the datafile } # try catch{ } # Catch } # process end{ Send-MailMessage @mail Remove-Item $LogFile } # end } #function UpdateMcKeanManagersSharePointGroups Function Write-LogError { <# .SYNOPSIS Writes error message to specified log file .DESCRIPTION Appends a new error message to the specified log file. Automatically prefixes line with ERROR: .PARAMETER LogPath Mandatory. Full path of the log file you want to write to. Example: C:\Windows\Temp\Test_Script.log .PARAMETER Message Mandatory. The description of the error you want to pass (pass your own or use $_.Exception) .PARAMETER TimeStamp Optional. When parameter specified will append the current date and time to the end of the line. Useful for knowing when a task started and stopped. .PARAMETER ExitGracefully Optional. If parameter specified, then runs Stop-Log and then exits script .PARAMETER ToScreen Optional. When parameter specified will display the content to screen as well as write to log file. This provides an additional another option to write content to screen as opposed to using debug mode. .INPUTS Parameters above .OUTPUTS None .NOTES Version: 1.0 Author: Luca Sturlese Creation Date: 10/05/12 Purpose/Change: Initial function development. Version: 1.1 Author: Luca Sturlese Creation Date: 19/05/12 Purpose/Change: Added debug mode support. Added -ExitGracefully parameter functionality. Version: 1.2 Author: Luca Sturlese Creation Date: 02/09/15 Purpose/Change: Changed function name to use approved PowerShell Verbs. Improved help documentation. Version: 1.3 Author: Luca Sturlese Creation Date: 02/09/15 Purpose/Change: Changed parameter name from ErrorDesc to Message to improve consistency across functions. Version: 1.4 Author: Luca Sturlese Creation Date: 03/09/15 Purpose/Change: Improved readability and cleaniness of error writing. Version: 1.5 Author: Luca Sturlese Creation Date: 12/09/15 Purpose/Change: Changed -ExitGracefully parameter to switch type so no longer need to specify $True or $False (see example for info). Version: 1.6 Author: Luca Sturlese Creation Date: 12/09/15 Purpose/Change: Added -TimeStamp parameter which append a timestamp to the end of the line. Useful for knowing when a task started and stopped. Version: 1.7 Author: Luca Sturlese Creation Date: 12/09/15 Purpose/Change: Added -ToScreen parameter which will display content to screen as well as write to the log file. .LINK http://9to5IT.com/powershell-logging-v2-easily-create-log-files .EXAMPLE Write-LogError -LogPath "C:\Windows\Temp\Test_Script.log" -Message $_.Exception -ExitGracefully Writes a new error log message to a new line in the specified log file. Once the error has been written, the Stop-Log function is excuted and the calling script is exited. .EXAMPLE Write-LogError -LogPath "C:\Windows\Temp\Test_Script.log" -Message $_.Exception Writes a new error log message to a new line in the specified log file, but does not execute the Stop-Log function, nor does it exit the calling script. In other words, the only thing that occurs is an error message is written to the log file and that is it. Note: If you don't specify the -ExitGracefully parameter, then the script will not exit on error. #> [cmdletbinding(SupportsShouldProcess=$true)] Param ( [Parameter(Mandatory=$true,Position=0)][string]$LogPath, [Parameter(Mandatory=$true,Position=1,ValueFromPipeline=$true)][string]$Message, [Parameter(Mandatory=$false,Position=3)][switch]$TimeStamp, [Parameter(Mandatory=$false,Position=4)][switch]$ExitGracefully, [Parameter(Mandatory=$false,Position=5)][switch]$ToScreen ) Process { Write-Debug "LogError" #Add TimeStamp to message if specified If ( $TimeStamp -eq $True ) { $Message = '['+$([DateTime]::Now)+'] '+$Message } #Write Content to Log Add-Content -Path $LogPath -Value "ERROR: $Message" #Write to screen for debug mode Write-Debug "ERROR: $Message" #Write to scren for ToScreen mode If ( $ToScreen -eq $True ) { Write-Output "ERROR: $Message" } #If $ExitGracefully = True then run Log-Finish and exit script If ( $ExitGracefully -eq $True ){ Add-Content -Path $LogPath -Value " " Stop-Log -LogPath $LogPath Break } } } # Function Write-LogError Function Write-LogInfo { <# .SYNOPSIS Writes informational message to specified log file .DESCRIPTION Appends a new informational message to the specified log file .PARAMETER LogPath Mandatory. Full path of the log file you want to write to. Example: C:\Windows\Temp\Test_Script.log .PARAMETER Message Mandatory. The string that you want to write to the log .PARAMETER TimeStamp Optional. When parameter specified will append the current date and time to the end of the line. Useful for knowing when a task started and stopped. .PARAMETER ToScreen Optional. When parameter specified will display the content to screen as well as write to log file. This provides an additional another option to write content to screen as opposed to using debug mode. .INPUTS Parameters above .OUTPUTS None .NOTES Version: 1.0 Author: Luca Sturlese Creation Date: 10/05/12 Purpose/Change: Initial function development. Version: 1.1 Author: Luca Sturlese Creation Date: 19/05/12 Purpose/Change: Added debug mode support. Version: 1.2 Author: Luca Sturlese Creation Date: 02/09/15 Purpose/Change: Changed function name to use approved PowerShell Verbs. Improved help documentation. Version: 1.3 Author: Luca Sturlese Creation Date: 02/09/15 Purpose/Change: Changed parameter name from LineValue to Message to improve consistency across functions. Version: 1.4 Author: Luca Sturlese Creation Date: 12/09/15 Purpose/Change: Added -TimeStamp parameter which append a timestamp to the end of the line. Useful for knowing when a task started and stopped. Version: 1.5 Author: Luca Sturlese Creation Date: 12/09/15 Purpose/Change: Added -ToScreen parameter which will display content to screen as well as write to the log file. .LINK http://9to5IT.com/powershell-logging-v2-easily-create-log-files .EXAMPLE Write-LogInfo -LogPath "C:\Windows\Temp\Test_Script.log" -Message "This is a new line which I am appending to the end of the log file." Writes a new informational log message to a new line in the specified log file. #> [cmdletbinding(SupportsShouldProcess=$true)] Param ( [Parameter(Mandatory=$true,Position=0)][string]$LogPath, [Parameter(Mandatory=$true,Position=1,ValueFromPipeline=$true)][string]$Message, [Parameter(Mandatory=$false,Position=2)][switch]$TimeStamp, [Parameter(Mandatory=$false,Position=3)][switch]$ToScreen ) Process { #Add TimeStamp to message if specified If ( $TimeStamp -eq $True ) { $Message = '['+$([DateTime]::Now)+'] '+$Message } #Write Content to Log Add-Content -Path $LogPath -Value $Message #Write to screen for debug mode Write-Debug $Message #Write to scren for ToScreen mode If ( $ToScreen -eq $True ) { Write-Output $Message } } } # Function Write-LogInfo Function Write-LogWarning { <# .SYNOPSIS Writes warning message to specified log file .DESCRIPTION Appends a new warning message to the specified log file. Automatically prefixes line with WARNING: .PARAMETER LogPath Mandatory. Full path of the log file you want to write to. Example: C:\Windows\Temp\Test_Script.log .PARAMETER Message Mandatory. The string that you want to write to the log .PARAMETER TimeStamp Optional. When parameter specified will append the current date and time to the end of the line. Useful for knowing when a task started and stopped. .PARAMETER ToScreen Optional. When parameter specified will display the content to screen as well as write to log file. This provides an additional another option to write content to screen as opposed to using debug mode. .INPUTS Parameters above .OUTPUTS None .NOTES Version: 1.0 Author: Luca Sturlese Creation Date: 02/09/15 Purpose/Change: Initial function development. Version: 1.1 Author: Luca Sturlese Creation Date: 12/09/15 Purpose/Change: Added -TimeStamp parameter which append a timestamp to the end of the line. Useful for knowing when a task started and stopped. Version: 1.2 Author: Luca Sturlese Creation Date: 12/09/15 Purpose/Change: Added -ToScreen parameter which will display content to screen as well as write to the log file. .LINK http://9to5IT.com/powershell-logging-v2-easily-create-log-files .EXAMPLE Write-LogWarning -LogPath "C:\Windows\Temp\Test_Script.log" -Message "This is a warning message." Writes a new warning log message to a new line in the specified log file. #> [cmdletbinding(SupportsShouldProcess=$true)] Param ( [Parameter(Mandatory=$true,Position=0)][string]$LogPath, [Parameter(Mandatory=$true,Position=1,ValueFromPipeline=$true)][string]$Message, [Parameter(Mandatory=$false,Position=2)][switch]$TimeStamp, [Parameter(Mandatory=$false,Position=3)][switch]$ToScreen ) Process { #Add TimeStamp to message if specified If ( $TimeStamp -eq $True ) { $Message = '['+$([DateTime]::Now)+'] '+$Message } #Write Content to Log Add-Content -Path $LogPath -Value "WARNING: $Message" #Write to screen for debug mode Write-Debug "WARNING: $Message" #Write to scren for ToScreen mode If ( $ToScreen -eq $True ) { Write-Output "WARNING: $Message" } } } # Function Write-LogWarning # SIG # Begin signature block # MIIU8AYJKoZIhvcNAQcCoIIU4TCCFN0CAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB # gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR # AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUqwsVrcmYmhNG7FVWHyTqjXhN # K12ggg/gMIIEmTCCA4GgAwIBAgIPFojwOSVeY45pFDkH5jMLMA0GCSqGSIb3DQEB # BQUAMIGVMQswCQYDVQQGEwJVUzELMAkGA1UECBMCVVQxFzAVBgNVBAcTDlNhbHQg # TGFrZSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxITAfBgNV # BAsTGGh0dHA6Ly93d3cudXNlcnRydXN0LmNvbTEdMBsGA1UEAxMUVVROLVVTRVJG # aXJzdC1PYmplY3QwHhcNMTUxMjMxMDAwMDAwWhcNMTkwNzA5MTg0MDM2WjCBhDEL # MAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE # BxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKjAoBgNVBAMT # IUNPTU9ETyBTSEEtMSBUaW1lIFN0YW1waW5nIFNpZ25lcjCCASIwDQYJKoZIhvcN # AQEBBQADggEPADCCAQoCggEBAOnpPd/XNwjJHjiyUlNCbSLxscQGBGue/YJ0UEN9 # xqC7H075AnEmse9D2IOMSPznD5d6muuc3qajDjscRBh1jnilF2n+SRik4rtcTv6O # KlR6UPDV9syR55l51955lNeWM/4Og74iv2MWLKPdKBuvPavql9LxvwQQ5z1IRf0f # aGXBf1mZacAiMQxibqdcZQEhsGPEIhgn7ub80gA9Ry6ouIZWXQTcExclbhzfRA8V # zbfbpVd2Qm8AaIKZ0uPB3vCLlFdM7AiQIiHOIiuYDELmQpOUmJPv/QbZP7xbm1Q8 # ILHuatZHesWrgOkwmt7xpD9VTQoJNIp1KdJprZcPUL/4ygkCAwEAAaOB9DCB8TAf # BgNVHSMEGDAWgBTa7WR0FJwUPKvdmam9WyhNizzJ2DAdBgNVHQ4EFgQUjmstM2v0 # M6eTsxOapeAK9xI1aogwDgYDVR0PAQH/BAQDAgbAMAwGA1UdEwEB/wQCMAAwFgYD # VR0lAQH/BAwwCgYIKwYBBQUHAwgwQgYDVR0fBDswOTA3oDWgM4YxaHR0cDovL2Ny # bC51c2VydHJ1c3QuY29tL1VUTi1VU0VSRmlyc3QtT2JqZWN0LmNybDA1BggrBgEF # BQcBAQQpMCcwJQYIKwYBBQUHMAGGGWh0dHA6Ly9vY3NwLnVzZXJ0cnVzdC5jb20w # DQYJKoZIhvcNAQEFBQADggEBALozJEBAjHzbWJ+zYJiy9cAx/usfblD2CuDk5oGt # Joei3/2z2vRz8wD7KRuJGxU+22tSkyvErDmB1zxnV5o5NuAoCJrjOU+biQl/e8Vh # f1mJMiUKaq4aPvCiJ6i2w7iH9xYESEE9XNjsn00gMQTZZaHtzWkHUxY93TYCCojr # QOUGMAu4Fkvc77xVCf/GPhIudrPczkLv+XZX4bcKBUCYWJpdcRaTcYxlgepv84n3 # +3OttOe/2Y5vqgtPJfO44dXddZhogfiqwNGAwsTEOYnB9smebNd0+dmX+E/CmgrN # Xo/4GengpZ/E8JIh5i15Jcki+cPwOoRXrToW9GOUEB1d0MYwggVbMIIEQ6ADAgEC # AhBtTXK1yM9OwllPm0eMV3CgMA0GCSqGSIb3DQEBCwUAMH0xCzAJBgNVBAYTAkdC # MRswGQYDVQQIExJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAOBgNVBAcTB1NhbGZvcmQx # GjAYBgNVBAoTEUNPTU9ETyBDQSBMaW1pdGVkMSMwIQYDVQQDExpDT01PRE8gUlNB # IENvZGUgU2lnbmluZyBDQTAeFw0xNzAzMjMwMDAwMDBaFw0xOTAzMjMyMzU5NTla # MIGlMQswCQYDVQQGEwJVUzEOMAwGA1UEEQwFMjM0NTYxCzAJBgNVBAgMAlZBMRcw # FQYDVQQHDA5WaXJnaW5pYSBCZWFjaDEeMBwGA1UECQwVMjQxMyBIdW50cyBOZWNr # IFRyYWlsMR8wHQYDVQQKDBZNdXJkYW4gRW50ZXJwcmlzZXMgTExDMR8wHQYDVQQD # DBZNdXJkYW4gRW50ZXJwcmlzZXMgTExDMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A # MIIBCgKCAQEAi0G6vzn+MSvLUsPtwzrJ0JL/3hXnlwFcJAy0eMjWAEQ+IdSEBrFm # O4nJep9gWnWV5bsh5Zal/qwG92A0XrTpEe+wxUJKgEicWVVKJgFNfF+Sw25Ac1KB # DjiZKiyCuaT6N+Zu4Le1HgypskZKtRNUZHEGcG3phZEwIRxzROUqoqmLul1zwzFF # uU/qBbmsYEMq+B4ZFd7VqBcVusXLxwEb/i4xrBYp6uzM0FwdBZG0anT6lcemjBxt # fQ0n+ivxQM2GGqkpfS6WkbVb/2xeUDTAcAtpG2u3jUnEoQJUmj6/kN9hJh3r1DbP # MT8PY6leb72QR6y+EKj/Dfum+kp+He/siQIDAQABo4IBrDCCAagwHwYDVR0jBBgw # FoAUKZFg/4pN+uv5pmq4z/nmS71JzhIwHQYDVR0OBBYEFDZv/Sb6UOZXd+mYdeqj # bh3uv6g8MA4GA1UdDwEB/wQEAwIHgDAMBgNVHRMBAf8EAjAAMBMGA1UdJQQMMAoG # CCsGAQUFBwMDMBEGCWCGSAGG+EIBAQQEAwIEEDBGBgNVHSAEPzA9MDsGDCsGAQQB # sjEBAgEDAjArMCkGCCsGAQUFBwIBFh1odHRwczovL3NlY3VyZS5jb21vZG8ubmV0 # L0NQUzBDBgNVHR8EPDA6MDigNqA0hjJodHRwOi8vY3JsLmNvbW9kb2NhLmNvbS9D # T01PRE9SU0FDb2RlU2lnbmluZ0NBLmNybDB0BggrBgEFBQcBAQRoMGYwPgYIKwYB # BQUHMAKGMmh0dHA6Ly9jcnQuY29tb2RvY2EuY29tL0NPTU9ET1JTQUNvZGVTaWdu # aW5nQ0EuY3J0MCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5jb21vZG9jYS5jb20w # HQYDVR0RBBYwFIESZG11cnJheUBtdXJkYW4ubmV0MA0GCSqGSIb3DQEBCwUAA4IB # AQBIimXZ7wtpMw/YsgBNY+7uo25P53Xzy1A1InZkW0ZTMFyncV3v+ZO+sq+T/86D # EOAv7W6FsBqMMI23hnWfcVuViQkM1UKSazoRKIdN2BhVX42SSN7iYhdBY+Pcel51 # b5HhprHgO1PoW1G6ez2h6U7oJ5IQ44D4jXnuiNQ99zZGR/IdEowxPLQsIC1lxEYh # efd0k2ot2V4/BTy6ccL7AhA19AArPWtDWzdX8u9RW7Ao8e2Z075jcOzfCBzYTbSC # JfAJgWSx52cBmHnTOvlWAW7NaD80TLY4CyHJsRjEvnHSiwVDK2AJjnuNjHw4dY8c # YX7Wn/7ZFyhaJGxAovu2mEKzMIIF4DCCA8igAwIBAgIQLnyHzA6TSlL+lP0ct800 # rzANBgkqhkiG9w0BAQwFADCBhTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0 # ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RP # IENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNhdGlvbiBB # dXRob3JpdHkwHhcNMTMwNTA5MDAwMDAwWhcNMjgwNTA4MjM1OTU5WjB9MQswCQYD # VQQGEwJHQjEbMBkGA1UECBMSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHEwdT # YWxmb3JkMRowGAYDVQQKExFDT01PRE8gQ0EgTGltaXRlZDEjMCEGA1UEAxMaQ09N # T0RPIFJTQSBDb2RlIFNpZ25pbmcgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw # ggEKAoIBAQCmmJBjd5E0f4rR3elnMRHrzB79MR2zuWJXP5O8W+OfHiQyESdrvFGR # p8+eniWzX4GoGA8dHiAwDvthe4YJs+P9omidHCydv3Lj5HWg5TUjjsmK7hoMZMfY # QqF7tVIDSzqwjiNLS2PgIpQ3e9V5kAoUGFEs5v7BEvAcP2FhCoyi3PbDMKrNKBh1 # SMF5WgjNu4xVjPfUdpA6M0ZQc5hc9IVKaw+A3V7Wvf2pL8Al9fl4141fEMJEVTyQ # PDFGy3CuB6kK46/BAW+QGiPiXzjbxghdR7ODQfAuADcUuRKqeZJSzYcPe9hiKaR+ # ML0btYxytEjy4+gh+V5MYnmLAgaff9ULAgMBAAGjggFRMIIBTTAfBgNVHSMEGDAW # gBS7r34CPfqm8TyEjq3uOJjs2TIy1DAdBgNVHQ4EFgQUKZFg/4pN+uv5pmq4z/nm # S71JzhIwDgYDVR0PAQH/BAQDAgGGMBIGA1UdEwEB/wQIMAYBAf8CAQAwEwYDVR0l # BAwwCgYIKwYBBQUHAwMwEQYDVR0gBAowCDAGBgRVHSAAMEwGA1UdHwRFMEMwQaA/ # oD2GO2h0dHA6Ly9jcmwuY29tb2RvY2EuY29tL0NPTU9ET1JTQUNlcnRpZmljYXRp # b25BdXRob3JpdHkuY3JsMHEGCCsGAQUFBwEBBGUwYzA7BggrBgEFBQcwAoYvaHR0 # cDovL2NydC5jb21vZG9jYS5jb20vQ09NT0RPUlNBQWRkVHJ1c3RDQS5jcnQwJAYI # KwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmNvbW9kb2NhLmNvbTANBgkqhkiG9w0BAQwF # AAOCAgEAAj8COcPu+Mo7id4MbU2x8U6ST6/COCwEzMVjEasJY6+rotcCP8xvGcM9 # 1hoIlP8l2KmIpysQGuCbsQciGlEcOtTh6Qm/5iR0rx57FjFuI+9UUS1SAuJ1CAVM # 8bdR4VEAxof2bO4QRHZXavHfWGshqknUfDdOvf+2dVRAGDZXZxHNTwLk/vPa/HUX # 2+y392UJI0kfQ1eD6n4gd2HITfK7ZU2o94VFB696aSdlkClAi997OlE5jKgfcHmt # bUIgos8MbAOMTM1zB5TnWo46BLqioXwfy2M6FafUFRunUkcyqfS/ZEfRqh9TTjIw # c8Jvt3iCnVz/RrtrIh2IC/gbqjSm/Iz13X9ljIwxVzHQNuxHoc/Li6jvHBhYxQZ3 # ykubUa9MCEp6j+KjUuKOjswm5LLY5TjCqO3GgZw1a6lYYUoKl7RLQrZVnb6Z53Bt # WfhtKgx/GWBfDJqIbDCsUgmQFhv/K53b0CDKieoofjKOGd97SDMe12X4rsn4gxST # dn1k0I7OvjV9/3IxTZ+evR5sL6iPDAZQ+4wns3bJ9ObXwzTijIchhmH+v1V04SF3 # AwpobLvkyanmz1kl63zsRQ55ZmjoIs2475iFTZYRPAmK0H+8KCgT+2rKVI2SXM3C # ZZgGns5IW9S1N5NGQXwH3c/6Q++6Z2H/fUnguzB9XIDj5hY5S6cxggR6MIIEdgIB # ATCBkTB9MQswCQYDVQQGEwJHQjEbMBkGA1UECBMSR3JlYXRlciBNYW5jaGVzdGVy # MRAwDgYDVQQHEwdTYWxmb3JkMRowGAYDVQQKExFDT01PRE8gQ0EgTGltaXRlZDEj # MCEGA1UEAxMaQ09NT0RPIFJTQSBDb2RlIFNpZ25pbmcgQ0ECEG1NcrXIz07CWU+b # R4xXcKAwCQYFKw4DAhoFAKB4MBgGCisGAQQBgjcCAQwxCjAIoAKAAKECgAAwGQYJ # KoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEOMAwGCisGAQQB # gjcCARUwIwYJKoZIhvcNAQkEMRYEFAOtkEeGOIAH/NZzLsDp5HGYOCULMA0GCSqG # SIb3DQEBAQUABIIBAIV78npA5eYatzYMxf/QqRtMVnC/qdgDI7fzd+vL980dniyQ # lJUbxeeDeCAnDlJC6DPhEKfbFVJHeGj15rtI2YEiROZuOpNwvVxQhuzLnFVS5vxr # RmM01oAO8Kr+tx5fWWxfqZBOZdw0rHAxrkRs3FAQr024Zkvb1/8DRHD9VOsTpO/s # Zz+gg8qLt1DRkRIQdfe90WfVSYp3xFDnKJWYVQ6mAGCHoEWHWv6NK1hhsETrTjrr # Z/5sDlShdIT3D6Nj/leQxDvj25UfDn+qnbMjTdWqrUu2/fRR6H2ACOqqN7Ch42lR # U5dkLgcVN+v2stH/+Bf6hqyp1to4hDo7ChLLUcqhggJDMIICPwYJKoZIhvcNAQkG # MYICMDCCAiwCAQEwgakwgZUxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJVVDEXMBUG # A1UEBxMOU2FsdCBMYWtlIENpdHkxHjAcBgNVBAoTFVRoZSBVU0VSVFJVU1QgTmV0 # d29yazEhMB8GA1UECxMYaHR0cDovL3d3dy51c2VydHJ1c3QuY29tMR0wGwYDVQQD # ExRVVE4tVVNFUkZpcnN0LU9iamVjdAIPFojwOSVeY45pFDkH5jMLMAkGBSsOAwIa # BQCgXTAYBgkqhkiG9w0BCQMxCwYJKoZIhvcNAQcBMBwGCSqGSIb3DQEJBTEPFw0x # NzEwMDExMDQ2NTZaMCMGCSqGSIb3DQEJBDEWBBQixcBU0hicRTf6/SAgLE5SpycD # ojANBgkqhkiG9w0BAQEFAASCAQBPYJi+YLoT5d26zBh0vTSWYrK4gSgMJgjfBhM4 # nft++6FsCNiMOu0Q1PCR6zRicSwgz2v0oNd9EJRfXMUQmqm6fad42ibF6Ce3FtMc # fLcu1PN8hm/G3zrpZIho08tGMedSFuTTes8qT3VdfuONk+iGfVo/cTBlg8yOTe/7 # EM7F6aZuGhUk251BsmfkSV99OdOhGoaVz+jJ0rgNS89yOAtKyBRt+OtFYSYnubbG # 9dc28o2+k5m5QC+YWW3StnPuQqIro3oseXNW3eA1VfPlx8BjTuWpcoSHE2NKbxxY # TvmLsstBjdhOU4Nf/4o2aOujsGi6xXpaAKdTAlLMQxR5YL1n # SIG # End signature block |