Massive 'if' statement script, is there a better way?

Hello,

I have been tasked with constructing a mailer that will send staff information to certain people, based on their location and their roles. For instance:

$teamA = @()
$teamB = @()
$teamC = @()
$teamD = @()

$users = Import-CSV 'path_to_file.csv'

ForEach ($user in $users) { 

#DeptA
if (user.department -like 'deptA*' -and user.title -match "jobA") {$teamA += "Username: $($user.username)`r`nTitle: $($user.title)`r`nLocation: $($user.location)`r`nOther Stuff: $($user.stuff)"}
if (user.department -like 'deptA*' -and user.title -match "jobB") {$teamB += "Username: $($user.username)`r`nTitle: $($user.title)`r`nLocation: $($user.location)`r`nOther Stuff: $($user.stuff)"}
#and so on...

#DeptB
if (user.department -like 'deptB*' -and user.title -match "jobA") {$teamC += "Username: $($user.username)`r`nTitle: $($user.title)`r`nLocation: $($user.location)`r`nOther Stuff: $($user.stuff)"}
if (user.department -like 'deptB*' -and user.title -match "jobB") {$teamD += "Username: $($user.username)`r`nTitle: $($user.title)`r`nLocation: $($user.location)`r`nOther Stuff: $($user.stuff)"}
#and so on...

#now the mailer...

$body = @"
`r`n     
New Staff Accounts:

REPLACE

Do not reply to this email address.

Thank you!
     
"@

$email = @{

smtpserver = "myserver"
from = "New_Staff@my.org"
subject = "New Staff Stuff"    

}

if (-not [string]::IsNullOrEmpty($teamA)) {
Send-MailMessage @email -body $body.Replace("REPLACE",$teamA) -To "user.guy@email.com"
}

if (-not [string]::IsNullOrEmpty($teamB)) {
Send-MailMessage @email -body $body.Replace("REPLACE",$teamB) -To "user.guy@email.com","user.otherguy@email.com"
}

}

The thing is, I have 19 departments. Not just a and b shown here. Add on to this that…

$teamA will be emailed to personA
$teamB will be emailed to person a AND b
$teamC will be emailed to person c
$teamD will be emailed to person d AND e

This is going to be long script of many, many If statements…unless there is a better way?

Thank you for any input.

Use a Switch

Ok.

On which part? How will a switch account for variables including department + title + who to email?

Care to elaborate on how to effectively use a switch in this particular script?

I am not asking you to write any for me, but a bit more detail would be appreciated.

Each team variable would be evaluated and then depending on the team returned the switch will execute code specific to that team.

    https://technet.microsoft.com/en-us/library/ff730937.aspx

In your situation, I don’t think it matters if you use If statements or switches. It sounds like you have to account for every possible combination of department and job, and have code execute for it. Unless there is something in common amongst the various combinations, such as user_deptA@domain.com is always emailed for any DeptA/Job combo, I don’t see how you can trim down your code.

How about this:

$csv = Import-csv 'path_to_file.csv'
$TeamA = $csv | where {$_.department -match 'deptA' -and $_.title -match "jobA"}
$Teamb = $csv | where {$_.department -match 'deptA' -and $_.title -match "jobB"}
#and so on...

$TeamC = $csv | where {$_.department -match 'deptB' -and $_.title -match "jobA")
$TeamD = $csv | where {$_.department -match 'deptB' -and $_.title -match "jobB")
#and so on...

Boy do I feel really stupid. I got too focused on what was provided. Doh!

Why do you say that?

Currently, I think you are correct. I am interested to see suggestions of others ways, though.

This is a bit better. Instead of filling empty arrays with the user information based on condition, you are setting the variable on each line.

That saves some lines. But yes, I still need to account for each variation of dept + title + email recipient, with Where conditions instead of If statements.

The only code is sending an email to variable recipients based on user conditions.

I read the TechNet article that you linked when I started this script, but I could not put together how it works for this scenario.

I will have another look. Thanks

This method also negates my ability to customize the string held by $TeamA - because we are just throwing the results of the Where condition into the variable…correct?

Whereas in my initial script, I was forming $TeamA like “user.username,user.title,user.location,user.stuff”, which is a customized version of the results of the Where condition.

I’d try separating the email sending part of the script from the data so that it’s not hard coded into the script logic if the departments ever change. For example, you could have a Send-CustomEmail function and a hash table with all the user data. Loop through that hash table and send the relevant data into your Send-CustomEmail function.

Thanks for the idea.

In this scenario, the departments will not change. New users will be added to those departments, and that is who I am grabbing from the CSV. Those variables are covered programmatically.

What CAN change is the recipient of the email. If the dept head changes, for example, then I would need to change the email recipient to the new one.

But as a standard practice, I will definitely keep your suggestion in mind!

I just meant that Ricks code simplifies your code by taking out 19 lines where your empty team arrays would have been and simplifies getting the users into the proper teams. You still have 19 lines of it, but still a little shorter code than a whole bunch of If statements. As for sending out the e-mail, I “may” have found a solution for you to shorten your code so that you don’t have to have 19 If statements, and minimize the changes you’d need to make for the recipients. I’m about to head somewhere in a minute, but will continue to work on the solution tonight to see if I can get it to work.

Derek is right. It’s good to separate out your code into re-usable functions to a specific task. However, the solution I think I have I’ll just post it as one script and let you take it from there.

You’re right, at first glance, Ricks code does look shorter. But in the process it changes what gets loaded into $TeamA.

In my script, $TeamA is only the information from $users that I want in the array, formatted to make sense in an email. In Ricks alternate example, $TeamA is the full results of $user row that the Where condition.

Ok, here we go.

First of all, here is the sample file that lists the users information. In you original code, you imported it into the $csv variable.

UserName,Department,Title,Location
user1,DeptA,JobA,Dallas
user2,DeptA,JobB,Houston
user3,DeptB,JobA,Garland
user4,DeptB,JobB,Richardson
user5,DeptA,JobA,Frisco
user6,DeptA,JobA,Dallas
user7,DeptB,JobB,Houston
user8,DeptB,JobA,Garland

Next, I created another CSV file (Referred to as TeamsData.txt in my script below) that lists the team information. It includes the following.

  1. A label for each team (Team column). In the end, I found this wasn’t needed with what I did, but it’s still good to have if you want a visible label when you open the file to edit it.
  2. The message recipients (Recipient field). This is a comma-separated list of the addresses of the department heads you want to send the per-team emails to. You edit this list as these users change.

NOTE: If you need more than one recipient for an e-mail, make sure to do a comma-separated list in a set of double-quotes (“”) as you see for the example on the TeamB & TeamD rows.

tuser1@olympiangods99.com,tuser2@olympiangods99.com

  1. The Department that is part of the Department/Title filter that is applied per team.
  2. The Title that is part of the Department/Title filter that is applied per team.
Team,Recipient,Department,Title
TeamA,"tuser1@domain.com",DeptA,JobA
TeamB,"tuser1@domain.com,tuser2@domain.com",DeptA,JobB
TeamC,"tuser3@domain.com",DeptB,JobA
TeamD,"tuser3@domain.com,tuser4@domain.com",DeptB,JobB

Ok. Now for the piece-de-resistance. Hopefully, it works for you. It worked great for me. I, of course, filled in the necessary values for SmtpServer, From, & Subject, and added a few more parameters to make the Send-MailMessage command work against my server. As you add, or remove, teams, or change the department heads (i.e. message recipients), you just need to edit the TeamsData.txt file, not the script code.

$EmailArgs = @{
                SmtpServer = "myserver"
                From = "New_Staff@my.org"
                Subject = "New Staff Stuff"
              }

$MsgBody = @"
`r`n     
New Staff Accounts:

REPLACE

Do not reply to this email address.

Thank you!
    
"@

$UserData = Import-Csv -Path C:\Data\UserData.txt
$TeamsData = Import-Csv -Path C:\Data\TeamsData.txt

ForEach($Team in $TeamsData)
{
  $TeamResults = $UserData | Where{($_.Department -like "$($Team.Department)*") -and ($_.Title -match "$($Team.Title)")}
  If($TeamResults)
  {
    ForEach($Result in $TeamResults)
    {
      $BodyReplacementTxt += "Username: $($Result.UserName)`r`nTitle: $($Result.Title)`r`nLocation: $($Result.Location)`r`n`n"
    }
    $Recipient = $Team.Recipient -split ","
    Send-MailMessage @EmailArgs -Body $MsgBody.Replace("REPLACE",$BodyReplacementTxt) -To $Recipient
  }
  Remove-Variable TeamResults,BodyReplacementTxt,Recipient -ErrorAction SilentlyContinue
}

Hope this helps.

FYI, this is what the body of one of my test messages shows.

New Staff Accounts:

Username: user3
Title: JobA
Location: Garland

Username: user8
Title: JobA
Location: Garland


Do not reply to this email address.

Thank you!

Got rid of the comment line in my code. I wrote it, when I was going to use the Team column from the TeamsData.txt file.

Adding the -ErrorAction parameter to my Remove-Variable cmdlet.

Here’s a suggestion for another way. I like using dictionary (or hashtable, depending on your point of view :slight_smile: lookups for this sort of thing instead of writing tons of if or case statements.

The first step I’d do is generate a dictionary of the departments with another dictionary as each key’s value. Something like this:

$depts = @{}

65..90 | %{$depts.Add("Dept$([char]$_)", @{})}

foreach($team in $depts.GetEnumerator()) { 
    65..90 | %{$team.Value.Add("Job$([char]$_)", @())}
}

That will produce a dictionary like this, with each value in the main dictionary being another dictionary with an array as its value:

Name                           Value
----                           -----
TeamS                          {JobC, JobB, JobW, JobX...}
TeamQ                          {JobC, JobB, JobW, JobX...}
TeamR                          {JobC, JobB, JobW, JobX...}
TeamJ                          {JobC, JobB, JobW, JobX...}
TeamK                          {JobC, JobB, JobW, JobX...}
TeamI                          {JobC, JobB, JobW, JobX...}
TeamC                          {JobC, JobB, JobW, JobX...}
TeamA                          {JobC, JobB, JobW, JobX...}
TeamX                          {JobC, JobB, JobW, JobX...}
TeamO                          {JobC, JobB, JobW, JobX...}
TeamH                          {JobC, JobB, JobW, JobX...}
TeamN                          {JobC, JobB, JobW, JobX...}
TeamZ                          {JobC, JobB, JobW, JobX...}
TeamB                          {JobC, JobB, JobW, JobX...}
TeamP                          {JobC, JobB, JobW, JobX...}
TeamM                          {JobC, JobB, JobW, JobX...}
TeamG                          {JobC, JobB, JobW, JobX...}
TeamE                          {JobC, JobB, JobW, JobX...}
TeamV                          {JobC, JobB, JobW, JobX...}
TeamD                          {JobC, JobB, JobW, JobX...}
TeamY                          {JobC, JobB, JobW, JobX...}
TeamU                          {JobC, JobB, JobW, JobX...}
TeamW                          {JobC, JobB, JobW, JobX...}
TeamF                          {JobC, JobB, JobW, JobX...}
TeamL                          {JobC, JobB, JobW, JobX...}
TeamT                          {JobC, JobB, JobW, JobX...}

Then, using Kyven’s CSV for some data, associate each user with a department and title:

$csv = import-csv test.csv

foreach ($user in $csv) {
    $props = @{"Department" = $user.Department; "Location" = $user.Location; "Username" = $user.Username}
    $depts[$user.Department][$user.Title] += New-Object -TypeName PSObject -Property $props
}

Then you can look up each user based on their department and title:

> $depts[$user.Department][$user.Title]

Username Department Location
-------- ---------- --------
user1    DeptA      Dallas

In this case I used an array to stash the user objects in, but you could just as easily build strings or something else as the value for the $user.Title key.

You might build pre-defined lists of departments and titles to build your dictionaries. You go even further and generate your keys and values based on the CSV attributes, then insert the users that way rather than generating the combinations.

Ideas to consider…