Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AWS admin sign-up automation #45

Merged
merged 17 commits into from
Feb 2, 2024

Conversation

nataliejschultz
Copy link
Contributor

@nataliejschultz nataliejschultz commented Nov 8, 2023

PR for e-mission/e-mission-docs#1008

email-config.py currently:

  • Passes AWS credentials as environment variables
  • Extracts email addresses from configuration files
  • Checks for an AWS user pool associated with the configuration file (and can generate a new pool if this functionality is wanted)
  • Checks for users in the pool by email address

will:

  • Create accounts for users not in pool
  • Send a customized (based on config file details) welcome email (triggered by addition of user to pool) with MFA instructions and password reset request

Committing email-config.py.
@nataliejschultz nataliejschultz changed the title 📧 AWS admin sign-up automation AWS admin sign-up automation Nov 8, 2023
Just pass the name of the file into terminal as an argument. Script should do the rest.
@nataliejschultz
Copy link
Contributor Author

First draft of the script is working and ready for review :)

@nataliejschultz
Copy link
Contributor Author

@shankari I realized you're not mentioned here, so probably didn't get my update! The script is ready for review 😄

Copy link
Contributor

@shankari shankari left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nataliejschultz I don't see any indicating of the testing done.
I understand that you might not have automated tests for this, but I don't even see any indication of manual testing done. In the future, please indicate testing done before sending it to me for review so I know that it is worth reviewing

Other changes below...

email_automation/email-config.py Show resolved Hide resolved
email_automation/welcome-template.txt Outdated Show resolved Hide resolved
email_automation/welcome-template.txt Outdated Show resolved Hide resolved
email_automation/welcome-template.txt Outdated Show resolved Hide resolved
email_automation/email-config.py Outdated Show resolved Hide resolved
email_automation/email-config.py Outdated Show resolved Hide resolved
email_automation/email-config.py Outdated Show resolved Hide resolved
email_automation/email-config.py Outdated Show resolved Hide resolved
email_automation/email-config.py Outdated Show resolved Hide resolved
email_automation/email-config.py Outdated Show resolved Hide resolved
Push addresses the clean-up changes requested.

Adds a main function for code that should not be run outside of the file.

Adds authentication for sts client.

Simplifies get_userpool_name and user_already_exists functions

Updates format_email function to add further customization of welcome email based on admindash_prefs section of config
Removing an unnecessary print
email_automation/email-config.py Outdated Show resolved Hide resolved
email_automation/email-config.py Outdated Show resolved Hide resolved
email_automation/email-config.py Outdated Show resolved Hide resolved
email_automation/welcome-template.txt Outdated Show resolved Hide resolved
email_automation/welcome-template.txt Outdated Show resolved Hide resolved
email_automation/email-config.py Outdated Show resolved Hide resolved
Copy link
Contributor

@shankari shankari left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks much better. I have some suggested text changes here.
I think that this should be ready to merge now as a standalone script - why is this still in draft mode?
@nataliejschultz if it is ready to merge, please move it out of draft.

After this is merged, you can create a separate PR for the GitHub action, which can use OIDC/IAM to log in.
That follow-on PR should also change the intake form to allow participants to specify admin users

- Added section to README emphasizing the need to install boto3 + other instructions on manually running script.

- Updated welcome email template's admindash link, as well as modified sentences per Shankari's suggestions.

- email-config.py: Removed comment, updated how email addresses are accessed in the config based on Abby's changes, and updated more welcome email language based on Shankari's suggestions.
@nataliejschultz nataliejschultz marked this pull request as ready for review December 4, 2023 23:24
Copy link
Contributor

@shankari shankari left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • One real cleanup (the URL for the admin).
    Multiple future cleanups. I would not have held up the release just for these, but since I am sending the PR back anyway, please get them handled as well.

README.md Outdated Show resolved Hide resolved
README.md Outdated Show resolved Hide resolved
email_automation/welcome-template.txt Outdated Show resolved Hide resolved
email_automation/email-config.py Outdated Show resolved Hide resolved
main.yml will work with our IAM role in AWS to connect Cognito and SES with GitHub Actions
This workflow should run when a new file is added to the config directory. The dependency (Get config name) should get the name of the newly added file, which is passed in during Run email-config.py.
Updating the url to the admin dashboard in the welcome email.
Adding a line about installing boto3 in a venv or conda env as opposed to implying that it must be installed on the user's local machine.
@shankari
Copy link
Contributor

I would like you to support both. It sounds like the main difference is in authenticating to AWS. So you would have
auth_for_gh_actions and auth_for_local_run and have the rest of the code be the same.
You would invoke auth_for_gh_actions or auth_for_local_run either by seeing which environmental variables were present, or by using argparse to have people pass in -g for gha and -l for local or something.

 * email-config.py:

1. Adds support for -l vs -g args Modified client setup + get_verified_art function to be compatible with the specified arg.

2. Fixes issue with link to admin dashboard. It works now :)

3. Removed a section of the update_user_pool function per Jianli's request, since it was unnecessary.

4. Modified to allow full path to config file as input

* Updated README to reflect the local run `-l` arg, as well as the path to the config file.

* Removed the two workflow files, since they will be part of a different PR.
Updates to the script due to the work going on to automate the script.

Mainly updates working around the file path issues I've encountered in GitHub Actions.
Copy link
Contributor

@shankari shankari left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Almost there!

email_automation/email-config.py Outdated Show resolved Hide resolved
email_automation/email-config.py Outdated Show resolved Hide resolved
email_automation/email-config.py Outdated Show resolved Hide resolved
using filepath_raw instead of relpath
removed print!
@shankari
Copy link
Contributor

shankari commented Dec 21, 2023

@nataliejschultz This looks good to me overall and I am ready to merge it after the task below.
Can you please update with "Testing done" (note #45 (comment)) and move it to "Ready for review" once done?

Path to the welcome template file needs to use maindir variable for both -l and -g runs.
@nataliejschultz
Copy link
Contributor Author

@nataliejschultz Can you please update with "Testing done" (note #45 (comment)) and move it to "Ready for review" once done?

I found an error with running the script from different directories while testing. Testing is now done, the script is updated, and the PR is back in ready for review! I think it's all ready.

nataliejschultz added a commit that referenced this pull request Dec 21, 2023
Updating to be up-to-date with latest push in the [other PR](#45)
@shankari
Copy link
Contributor

shankari commented Jan 21, 2024

@nataliejschultz wrt "Can you please update with "Testing done"" I would like you to actually list the testing done.
e.g.

.Testing from the same directory

more tests
more tests
....

@nataliejschultz
Copy link
Contributor Author

I would like you to actually list the testing done.

I removed myself from the account between each test to see if it would get through the entire process:

running script directly:

python email-config.py -l ../configs/wyoming.nrel-op.json
myemail@nrel.gov not in user pool! Creating account...
Account created! Sending welcome email.

running from one directory up:

 python ./email_automation/email-config.py -l configs/wyoming.nrel-op.json 
myemail@nrel.gov not in user pool! Creating account...
Account created! Sending welcome email.

running at config file location:

python ../email_automation/email-config.py -l wyoming.nrel-op.json 
myemail@nrel.gov not in user pool! Creating account...
Account created! Sending welcome email.

running from far away directory:

python Documents/GitHub/forked-nrel-openpath-deploy-configs/email_automation/email-config.py -l Documents/GitHub/forked-nrel-openpath-deploy-configs/configs/wyoming.nrel-op.json 
myemail@nrel.gov not in user pool! Creating account...
Account created! Sending welcome email.

I got an email each time!

Fixing the parsing issue for emails that we encountered during demo!
@nataliejschultz
Copy link
Contributor Author

I added functionality to remove users who are not in the config file, but are already in the user pool. Previously, I created the user_already_exists function:

def user_already_exists(pool_id, email, cognito_client):
    try:
        response = cognito_client.list_users(UserPoolId=pool_id)
        users = response["Users"]
        result = False
        if str(users).find(email) > 1:
            result = True
        return result
    except ClientError as err:
        logger.error(
            "Couldn't list users for %s. Here's why: %s: %s",
            pool_id,
            err.response["Error"]["Code"],
            err.response["Error"]["Message"],
        )
        raise

this function takes users, which is a list of dictionaries containing info on each user in the pool, and searches for a string matching the desired email.

For the new use case, I modified it a bit to create get_users:

def get_users(pool_id, cognito_client):
    try:
        response = cognito_client.list_users(UserPoolId=pool_id)
        return response["Users"]
    except ClientError as err:
        logger.error(
            "Couldn't list users for %s. Here's why: %s: %s",
            pool_id,
            err.response["Error"]["Code"],
            err.response["Error"]["Message"],
        )
        raise

This function is called early on in the program, and gives us the users variable:
users = get_users(pool_id, cognito_client)

Next, each dictionary in users is looped over, and the email is isolated as user_email.

    for user in users:
        for attr_dict in user["Attributes"]:
            if attr_dict["Name"] == "email":
                user_email = attr_dict["Value"]
                if user_email not in emails:
                    remove_user(pool_id, user_email)
                    print(f"{user_email} removed from pool.")

If the user_email is not in the emails list that was pulled from the config file, another new function named remove_user is called:

def remove_user(pool_id, user):
    response = cognito_client.admin_delete_user(
        UserPoolId= pool_id,
        Username= str(user)
)

I tested this by adding myself to the pool:

email1@nrel.gov not in user pool! Creating account...
Account created! Sending welcome email.

Then trying to re-add myself AND another new email,

email1@nrel.gov already in user pool!
email2@yahoo.com not in user pool! Creating account...
Account created! Sending welcome email.

And finally, removing my first email from the config to see what happens:

email1@nrel.gov removed from pool.
email2@yahoo.com already in user pool!

I checked the user pool, and my email1@nrel.gov was indeed removed from the pool! I decided to try and follow the welcome email to log in with my removed email, just to see what would happen. I got the following message:
Screenshot 2024-01-31 at 11 20 25 PM

@nataliejschultz
Copy link
Contributor Author

I added functionality to remove users who are not in the config file, but are already in the user pool.

@shankari I haven't pushed these changes yet since this PR is still open; I am happy to put it in a separate PR once this one is merged, or just push it to this one. Wanted to check with you beforehand.

@shankari shankari merged commit 724c285 into e-mission:main Feb 2, 2024
shankari pushed a commit that referenced this pull request Jun 26, 2024
* Test push

:)

* Init commit for main.yml + email-config.py

main.yml will authenticate AWS with our IAM role, use a dependency to get the name of the latest file pushed to configs directory, and run email-config.py.

Pushing email-config.py as it was in the last commit to my  nataliejschultz:AWS-email-config PR.

Reverting README.md to before test push.

* configs push test

Testing to see if pushing to the configs directory runs the main.yml workflow!

* Updating main.yml

main.yml didn't run on my last push (though I thought it would). Updating when the workflow runs and trying again.

* Another test push

Trying to get workflow to run. If this doesn't work, it might be because my PR is a draft? I've seen conflicting info about how to get a workflow to run on a non-main branch in PR phase.

* Region error

trying to fix `Error: Region is not valid: <"us-west-2"> `

* Push test

Changing file to trigger workflow.

* Fixing typo

Typo was causing secrets access issue.

* Push test

* Changing ARN syntax + push test

Hoping that previous run is just a syntax error that I'm correcting now.

* Changing option in run + push test

`m` is not an option for the command, so I added a different one.

* Creating clients + push test

I'm not sure how to set up the client when authenticating through IAM. Trying this out.

* Modifying cognito client + push test

Adding region to cognito client + moving where environment variable is accessed.

* Combining jobs to pass credentials

I don't think the jobs can be separated if I want to pass the credentials from my AWS auth step to the run step. Going to try combining them and see what happens!

* Fixing dependency

I don't think this dependency worked because of where `id` was in the workflow. Trying again!

* Testing TJ dependency

Testing out another dependency to see if it's better than jitterbit :)

* Push test

Testing out TJ dependency with this push!

* Config file export

Modified the `changed_files` job to include a for loop that exports an environment variable with the name of the changed file. The variable is then passed in while running the python script. Hoping this works!

* Push test

* Typo

Forgot to change value of file from changed_file. Oops.

* Push test

* Adding prints

Adding some prints for troubleshooting to see if the name of the config file is being passed properly or not.

* Push test

* Trying out another way to access config file

Accessing environment variable (hopefully) the right way.

* Trying to get jobs to run sequentially

* Another way to pass config file name

Trying another way to pass the config file name between the two jobs.

* Prints

prints to figure out why the filepath isn't correct

* Another print

Adding another print.

* Config relative file path change

The relative path is slightly different in github actions. Adding an if else line to change depending on -l or -g arg

* Using full path to file

Relative paths seem to have an issue in GHA. trying full path to file.

* Push test after updated settings

Last error was due to AWS permissions not working. Jianli updated the settings, so we're going to check and see if it works now.

* Push test

Trying again per jianli

* Updating config path for -g + removing prints

Last run worked! I removed myself from the user pool, so I'm going to try again with this updated method of getting the config path for -g runs + trying to actually add myself to the user pool.

* Adding welcome template

Forgot that I needed to add the welcome template file! Adding :)

* File not found?

Got `FileNotFoundError: [Errno 2] No such file or directory: 'welcome-template.txt'` on last run. Pushing a change to make sure the file is there and going to try again.

* Push test

* Adding pwd

Added os.getcwd() to see why it's not opening the welcome template file.

* welcome template access

adding a workaround for the filepath issue

* Filepaths again

Trying another fix.

* Previous issue fixed. Now sts client

Adding an empty defintion for sts_client with -g since it has to be passed into the get_verified_arn function.

* Push test

Testing now that the AWS settings have been updated !

* Updating script per changes  +emoving changes to readme

Updating email-config so it's up to date with the other email-config.py in the other PR.

Removing the PR's changes to the Readme (not sure how they got on there)

* Readme

Reverting readme?

* Updating email-config.py

Updating to be up-to-date with latest push in the [other PR](#45)

* Renaming workflow

Giving main.yml a more meaningful name (AWS-auth.yml)

* Push test

testing workflow under new name before removing email :)

* Removing email

Removing email address from wyoming file. Workflow will probably raise an error on this run.

* Trying to fix readme changes

* Trying to remove all changes to readme

I tried git checkout, but that didn't give me a pushable option to remove my previous commit changes. Let's see how this does :)

* Removing duplicate files

Since the final test worked fine, we can remove them from this PR.  They should be merged on the other PR first, and then this PR can be closed.

* rename for demo

* re-adding files for demo

* adding wyoming config file

* Email parsing fix

After demoing with Abby, we found out that the emails weren't being parsed correctly due to differences in our previous configs vs the new generation method. Fixing and testing!

* Delete wyoming.nrel-op.json

* Add wyoming config

* Restoring wyoming original config

Restored the old wyoming config + removing files that were temporarily re-added for the demo

* What happens when modifying two configs in one push?

Trying to see what happens when we update two config files at once. Will the value of the github output be formatted as a string? Will it be a comma separated list?

* echo multiple filename change output

Checking output when we change multiple configs at once

* push test

* Push test

Trying to activate the GH actions workflow

* Changing bash filename handling

Modifying the GHActions script to (potentially) handle multiple changed config files.

* Array appending debugging

Array did not work for bash. Checking out it's being output.

* Array formatting

Modifying array formatting so it's hopefully what I want! Also seeing if I can read the actual output, though it might produce a context error.

* Moving things around

moving some echoes.

* Array syntax

Seeing if the array will echo properly with this syntax change.

* Testing passing two changed files

Modified the GH Actions to be able to pass in and loop over two config files. We'll see if it works by temporarily adding in email-config.py with a relevant print and seeing what happens!

* Syntax?

I'm not sure what went wrong the first time with bash. Adding some echoes to figure it out.

* More subtle bash syntax

As it would turn out, bash scripting is finicky when dealing with arrays. My array is not being sent to the GitHub output properly for some reason. Trying it out by replacing * with @. If that doesn't work, I'm going to try removing the entire [@] section and see what happens.

* More syntax, whoops!

I changed CONFIG_FILE to CONFIG_FILES at one point and didn't change it in the outputs section. Now let's see if it works!

* Bug catching

Caught a bug in my email-config.py script where it wasn't returning is_userpool_exist properly. Let's see if this fixes things.

* Commenting out

Want to merge recent changes to my branch, but don't want to re-run all these configs!

* renaming email config

renaming to merge from main.

* email-config.py bug fix

Fixing a bug I found a few pushes ago.

* Two changed files, removal test

Testing removal of my email from the wyoming pool, in addition to adding myself to nrel-commute pool in the same action.

* See last commit. Running job

Job wasn't picked up on last push. Trying again.

* action run only on push to main

changing branch that workflow runs on push to (main)

* Reverting wyoming config

Reverting Wyoming config file

* WY format

Continuing reverting WY file

* syntax

blank space removal

* Update wyoming.nrel-op.json

Adding blank line after file

* Reverting commute config

Reverting to original commute config
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants