JiraPS
JiraPS copied to clipboard
Sessions and server URLs
There are some pretty significant drawbacks to the current config.xml structure:
- If PSJira is installed system-wide, the user's PowerShell session must have rights to write to
$env:ProgramFiles\WindowsPowerShell\Modules\PSJira
in order to modify the file (typically means admin rights) - No easy way to alternate between more that one JIRA server without modifying the config file each time
- XML is awkward (my opinion)
The only major pro to this system is that you can set it up once and forget about it if you never need to change JIRA instances. Admin rights are required once, to set up the file, but aren't required each time you use the module.
I'm interested in re-thinking this structure to try to reduce those negative points. In no particular order, here are some ideas:
-
Multiple config files
- User might need to specify a config file on a regular basis (add the -ConfigFile param to more functions).
-
Saving server URL in JiraSession object and allowing user to specify a session via parameter
- Something like this:
# Example 1 $session = New-JiraSession -Credential (Get-Credential) -ServerUrl 'https://jira.example.com' Get-JiraIssue TEST-01 -Session $session
- User would need to enter the URL when creating a session - this would affect normal use of the module. That's my biggest hesitation with this method, since it would be a breaking change.
- Above could be mitigated by adding New-JiraSession to the user's PowerShell profile, but that would require user saving credentials to disk as well (for the -Credential param)
- Users who use the -Credential param instead of creating a session would need an alternate method of providing the server URL, so anything with the -Credential param might need a -Url param as well
- Could still save the most recently used session in module PrivateData...maybe Get- and Set-JiraSession?
# Example 2 $session = New-JiraSession -Credential (Get-Credential) -ServerUrl 'https://jira.example.com' $session2 = New-JiraSession -Credential (Get-Credential) -ServerUrl 'https://jira2.example.com:8080' Set-JiraSession $session Get-JiraIssue TEST-01 # Uses $session Set-JiraSession $session2 Get-JiraIssue FOO-99 # Uses $session2
-
Support for multiple server URLs in config.xml
- Provide a name or ID for each URL, then add a -Instance param to functions
# Example 3 Set-JiraConfigServer -Instance 'prod' -Url 'https://jira.example.com' New-JiraSession -Credential $cred -Instance 'prod' Get-JiraIssue TEST-01 # will use the stored Session for the prod instance
...or...
# Example 4 Set-JiraConfigServer -Instance 'prod' -Url 'https://jira.example.com' New-JiraSession -Credential $cred -Instance 'prod' Get-JiraIssue TEST-01 -Instance 'prod' # If prod doesn't have a saved session, use anonymous access (with a Write-Warning message)
- Doesn't really make alternating between sessions any easier, unless I added Get- and Set-JiraSession to update the stored session with one from a variable
-
Configuration module for PowerShell
- Introduces a module dependency
- Solves the XML and permissions issues, but doesn't address switching between environments - I'd need to combine this with one of the above examples
Thoughts and opinions welcome. Anyone care to chime in on this?
Right now, I'm leaning towards the second option above - modifying the JiraSession object to include a server URL. Here's a mock-up of how it would behave (similar to Example 2 above):
# This session is authenticated...
$session = New-JiraSession -Credential (Get-Credential) -ServerUrl 'https://jira.example.com'
# ...and this one will use anonymous access.
$session2 = New-JiraSession -ServerUrl 'https://jira2.example.com:8080'
# Use $session to talk to the first instance
Set-JiraSession $session
Get-JiraIssue TEST-01
# Use $session2 to connect to the second instance
Set-JiraSession $session2
Get-JiraIssue FOO-99
# Still uses $session2, since that was the last-used session
Get-JiraIssue FOO-101
# Now use $session as a temporary override with the -Session parameter...
Get-JiraIssue TEST-01 -Session $session
# ...but the "saved" session is still $session2
Get-JiraIssue FOO-99
So in a bit more detail, here's the proposed solution:
- Add a -Url parameter to New-JiraSession and allow JiraSession objects to store the URL to a JIRA instance.
- Ensure New-JiraSession outputs the session object. Update examples to demonstrate saving this to a variable.
- Ensure that Get-JiraSession and Set-JiraSession will get and set the Session object in module PrivateData. Document these functions a bit better.
- Add a -Session parameter everywhere that the -Credential parameter exists now.
- This will not be required - if the session does not exist, Invoke-JiraMethod will look for a session in PrivateData as it does now. If a session is not provided and not found in PrivateData, an error will be thrown.
- A session object provided via the -Session parameter will take priority over a saved session in PrivateData
- A session object provided via the -Session parameter will NOT overwrite the saved session in PrivateData. To change the "active" session, use Set-JiraSession.
- Drop the -Credential parameter. It's now obsolete.
- Leaving this parameter wouldn't do any good unless I also added a URL parameter to each function. Without the config file, you'd need to supply the URL each time you called the function. This would also make it more difficult to manage parameter sets, since Session would be mutually exclusive with Credential and URL.
- Delete functions and parameters relating to the config.xml file, as they will no longer be needed.
This will introduce a breaking change to any users who rely on the -Credential parameter. These users will need to add a New-JiraSession line to the top of any script that uses credentials. That's still my biggest hesitation in this change, but honestly, the more I look at it, the better this idea looks. The best thing I can do in that case is document the heck out of it and make it clear in the release notes that a change has happened.
I doubt anyone else goes through the issues for this project as much as I do, but just in case, does anyone have a different opinion before I start coding this?
Disclaimer: You're not breaking anything I'm doing, so I'm on board.
I have a suggestion, though. At the end of the rewritten New-JiraSession
, are you open to something like this?
if ((Get-JiraSession).Count -eq 1)
{
Get-JiraSession | Set-JiraSession
}
This would default the first session to active. I'd argue that this would be expected behavior, especially for those only interacting with one server.
I sort of like just having to add credentials and not worry about which instance I'm connecting to, or maybe a list of common servers?
Similar to how a .ssh file is created after a session, perhaps a list of sessions with a friendly name, actual url, and an auth reference could be stored?
Hypothetically:
New-JiraSession (Get-MyJiraServers "my jira instance name") or possibly New-JiraSession -friendlyname "an easy server name from get-myjiraservers"
Or something crazy like this, If GetMyJiraServers returns several different servers, spawn several sessions at once...
New-JiraSession (Get-MyJiraServers)
Couple this with use of the jsessionid cookie for continued interaction for servers would be very handy.
The ".jira" file in the user profile would hold all the servers you've dealt with. The myinvocation privatedata from New-JiraSession would carry the jsessionid, keeping it secure.
I think ultimately, using basic, cookie, and oauth would be extremely beneficial.
CONSIDERATION: We have multiple business-critical automated tasks running simultaneously as scheduled tasks. Each scheduled task is configured to use the same admin user account and profile.
Initially we had written the PowerShell scripts to create a new Jira session at the start and then close the session prior to exiting. However as the number and frequency of scripts grew we started to get issues accessing Jira.
All scripts use the following code:
try { $js = New-JiraSession -Credential $Credential -ea Stop } catch { Write-Host "ERROR: Unable to create new Jira session - check passed credentials." -f Red Write-Host "$($global:Error[0].exception.message)`n" -f Gray $ok = $false }
As part of our investigation we modified the scripts to use the -Credential parameter when calling PSJira cmdlets without a predefined session. To date this has seemed to have solved the problem. So you can understand our hesitation to change what is working.
We certainly support feature improvements but not at the risk of initiating breaking changes without minimal backwards compatibility. Any suggestions you may have would be appreciated.
Some areas that we do not have sufficient information on which may help is:
- Is there a limit on the number of simultaneous sessions that can be created?
- Does each session need to be removed after use or will obsolete data be stored in the module PrivateData?
- How will the active session be flagged in the module PrivateData to support scripts running simultaneously?
Just some thoughts... thanks for your efforts on this. Kind regards.
What if you just moved the XML to $env:appdata
? This makes it a per user configuration. Also easy enough for us to copy into the profile of a service account. Could also consider using JSON instead of XML because powershell handles that so much easier.
It is definitely not a bad idea to add more options when creating new sessions. I think I could adjust very easily if the config file vanished. Just make sure you add a good clean example in the help examples and I will be able to figure it out. Possibly add a warning if you detect the old config file for those that need to troubleshoot old scripts.
I would end up adding this to my profile to replace the config file once I realized what happened:
$PSDefaultParameterValues['New-JiraSession:ServerURL'] = $myURL
Awesome, glad to hear some discussion, and thanks for the sanity check!
@aaronsb makes a really good point - this change would all but remove the ability to use basic authentication without cookies, and that's probably a bad thing. Also, if I'm ever able to wrap my mind around OAuth and add support for that, it would be helpful to be able to save that token to disk somewhere instead of expecting users to put it in their PS profile or script somewhere. So it looks like totally removing a config file is probably not the best plan. I may still implement some extra functionality in Session objects, but that doesn't mean credentials and a saved server URL should go away.
That said, the original issues with the config file are still legitimate problems. As @KevinMarquette mentioned, JSON is pretty easy since PS has native support for it, so that will work better than XML. I also agree that $env:APPDATA
is probably as good a place as any - it certainly solves the admin problems. (I also really like the idea of looking for the old config file and writing a warning message, just in case.)
Here's what I'm thinking now. This would be a config file stored in $env:APPDATA (something like PSJira.json):
{
"servers": [
{
"name": "MyServer"
"url": "https://jira.example.com",
"default": true,
},
{
"name": "YourServer",
"url": "https://someotherjira.foobar.com:8080"
}
]
}
And here are some examples using that config file:
# No credentials passed: anonymous access to the default server (MyServer)
Get-JiraIssue TEST-01
# Credentials passed: basic auth to the default server
Get-JiraIssue TEST-02 -Credential $cred
# Creating a session can allow you to contact a different server
$s = New-JiraSession -ServerName 'YourServer' -Credential $cred
# Passing the session variable uses that session, including URL and credentials
Get-JiraIssue FOO-01 -Session $s
The best part about this is that it wouldn't break anything at all. It would require re-creating the configuration file once, but there wouldn't be any changes needed to existing scripts. If a URL or server name is ever unspecified (whether using sessions or basic auth), the module would use whatever was set as the default server in the config.
I also like the idea of being able to define "friendly names" for servers in the config file. That keeps users from needing to retype or copy/paste long URL's when creating authenticated sessions.
Also, to respond to some more specific concerns:
@brianbunke Absolutely, though with my new ideas above that should be a non-issue.
@colhal Thanks for the feedback. Your concerns, along with @aaronsb, helped me reconsider and drove my previous post. If that suggestion goes though, there won't be any changes to current functionality - users will only need to re-create a configuration file once.
As far as your specific questions, here's what I know:
- Each session is roughly equivalent to a user logging into JIRA via browser, and I don't know of any software or config limits JIRA has for this. Do make sure to close any JIRA sessions after you're done. You can also take a look at the user timeout settings in JIRA to make sure that any dangling sessions eventually get closed. My guess would be that you're hitting a hardware or database limitation on your JIRA instance, but that's really only a guess.
- Module PrivateData is specific to each PowerShell instance. I would still recommend using Remove-JiraSession to log out of JIRA when a script completes, but that's for JIRA's sake, not for PowerShell's sake. Each new instance of powershell.exe has a new, blank slate.
- My thought is that the module PrivateData would only store a single session at a time. If the user wants to juggle several sessions, they would save them to variables and reference them as needed. That said, my comment above has changed the way I'm addressing this, and I don't believe this should be an issue.
I would always recommend testing the module thoroughly before updating it in a mission-critical environment like you've described.
@KevinMarquette Yes, some additional documentation is in order. I just created #51 so I don't lose sight of this; I'll probably try to focus on that as soon as I come to a conclusion on this issue.
Thanks for your reply - happy to see the direction you are taking. Just one more question:
- As we never use sessions but instead pass credentials to each PSJira cmdlet as they are being used - do you see a problem with this approach? Interested in your thoughts.
Thanks for your response. Regards.
The biggest issue with this approach is that you're using HTTP basic authentication for each call to JIRA. That's not always a problem, just something to be aware of - it would be relatively simple to sniff that traffic and obtain the password for that user account. If you've got other security in place, more than likely it won't be an issue, but I'd suggest rotating that password on a regular basis.
Creating a session also uses basic authentication once for the initial connection, but it uses a cookie to track an authenticated session from that point on. This means your credentials are used once, but not used with each request to JIRA.
I would like to point out that using basic auth, every time, can become slow with lots of calls. Re-use of the jsessionid (presenting when connecting) skips the calls to crowd, and potentially re authenticating all the way down the chain in a complex federated authentication environment.
I haven't really looked at the way a new session is created and kept open but if it's making a basic auth call on each action that would be pretty inefficient.
If you want, I can probably work out a simple POC for jsessionid keeping and renewal in powershell, and you can use that however you want.
EDIT: OMG I need to read more. @replicaJunction says right there "Creating a session also uses basic authentication once for the initial connection, but it uses a cookie to track an authenticated session from that point on. "
Anyway, creating a new auth session each time is slow, as @colhal seems to be using that.
Thanks for your comments. As already mentioned we had already tried using sessions but when running multiple simultaneous scripts we started getting random Jira authentication problems. Each script would create a new session, do its job and at the end of the script remove the session.
It seems that the process of creating and deleting sessions in one script would affect other running scripts. All scripts run as scheduled tasks at various times (some at 10 min intervals and allow multiple instances) and under the same admin account/profile.
We suspected that it may have something to do with the cookie creation but haven't pinpointed the problem. So:
- When simultaneous scripts run as per the above scenario are multiple cookies created or do they share the same cookie?
- What happens to the cookie each time a new-jirasession or remove-jirasession is called?
- If one script is ending and removes the session it initially created while another is part way through will the second script no longer have access to its session? (This is what we suspect was happening but we could be wrong.)
It should be mentioned that all these scripts run on utility servers with IE enhanced security enabled for administrators. It could be that the issue may be related to IE configuration settings (e.g. cookies).
Either way we would like to use sessions but to date have had to revert to using the credential parameter which has provided a reliable workaround.
Any suggestions you may have would be appreciated. Regards.
Hi @colhal, I'd like to keep this issue page on the topic of the module config file and pending updates to that system. Since it looks like I won't be removing the -Credential parameter after all, your current setup shouldn't break with any updates here. Please create a separate issue if you'd like to discuss your specific situation further. Thanks!
No worries, thanks for your efforts.
With my merge request #71 multiple sessions can be handled. It basically just adds a server attribute to the session information. The session will be chosen by using Get-JiraConfigServer and select the matching session.
To come back to the config file: I would prefer If the server will be stored non persistent (somewhere in memory for this powershell instance) and could optionally be written to a file (Set-JiraConfigServer <...> -persistent or so) Get-JiraConfigServer could handle this easily: If (Serverinmemory) {use it} else {read from file and write to non persistent store} In scripts, most people would be able to handle the server URLs. If the commands are used interactively you can start an admin-powershell once and use -persistent.
Storing of multiple server with friendly short names would be a plus. I prefer JSON
I've created another merge request (#81) to better handle ConfigServer and sessions.
With my two open merge request (#71 and #81) you can do the following:
-
Set ConfigServer "on the fly" without administrative permissions
-
Set ConfigServer persistent like before
- Set-JiraConfigServer <Server> -persistent
- Needs administrative mode if no custom location for config file is given
-
Store multiple sessions
-
If multiple sessions are stored that one that matches ConfigServer will be used.
- only one session per server but this should be fine for nearly all use cases I can imagine. Who wants to connect to one server with different usernames?
This should be a good foundation to change the file format to json, store multiple servers to the config file and others.
I haven't taken care of the strange appveyor errors until this discussion shows that this will / could be going to master.
For what it's worth, I think your session idea is great, it's very much like the Profile concept that's supported by the AWS PowerShell module for managing parameters to access AWS service endpoints, which is an analog to your session -- (e.g. URL, username, password, tokens, regions, etc.).
In fact I like this model so much for managing things of this nature, I've used it myself on one or two projects of my own with great success (at least in my opinion).
A couple features that I've incorporated in my most recent use of profiles:
- Ability to define a
default
session -- if no other session is specified, all commands that use a session look for the default, this would give you backward compatible behavior. - Ability to override most attributes captured in a session on a per call basis with command parameters -- I found this most useful in testing and debugging, where you may have multiple credential sets that all share a common set of attributes, but one or two differ, say the username, or just the URL, or say you don't want to persist some attributes in the session, but require them to be specified at invocation time, like the password or secret token.
I've begun the basic implementation of some of these ideas here. Feel free to peruse them and offer any comments (on this repo, please, not on the fork). Once I've gotten to the point where tests pass again, I'll clean things up and submit a PR for the change.
This will likely require a small change for end-users when all is said and done (a one-time conversion to the new config file format), but I anticipate it won't change normal use of the module for simple scenarios. It will enable many of the features discussed here.
@ebekker, I'm going over the links you provided and I think a default session is an absolute must. Thanks for the reference material!
@replicaJunction : does that mean we can close this PR?
If you don't mind, I'd rather leave it open until there's actually a PR for the changes.
I see you guys have consolidated all the PS modules for Atlassian products under one org -- I guess something like this change could become a common component of all the modules. Is that what the AtlassianPS project is intended for?
Not primarily. I do see potential for it, but the motivator was sharing human resources and practices
It might not be a bad idea to store any config data in the same place, though. Currently I'm working on the premise that the config file will go in $env:APPDATA. Of course, I need to rename that directory from PSJira to JiraPS - but would it make more sense to use a directory called AtlassianPS and name the config file JiraPS.config.json
or something?
I haven't looked closely over the other modules yet, so I'm not sure how much config data they need to store at the moment, but if nothing else, this could be a good move for future-proofing.
Other modules don't have anything similar for now. But I agree that the would be more elegant and Future proof
Hi I was working on my own version of a Jira CLI (https://github.com/Invoke-Automation/AtlassianCLI) and I implemented something that might work better then what is implemented right now for the sessions.
I use an AtlassianSession class that is initaly just created in the session but can be saved in an enc.xml file. I also include the credentials in this file (encrypted ofcourse). This has a huge benefit when it comes to automation. I simply generate the session file once and then I can automate a job that uses the server and credentials stored. (This only works for the account that generated the enc.xml file) It also works great when you want to switch between instances because you can just load the new session whenever you want. I was also struggeling with OAuth but I believe that this setup is compatible with the implementation of OAuth in time since you can store alot of info in it since it gets encrypted.
Only downside to this approach is that multiple users cant share session files since only the user that generated the file can deserialise it.
I would be happy to help implement this here (since I'm planning on putting my efford in this module rather then in my own)
I personally don't like storing passwords in a reversible way anywhere and won't include it in this module.
Reasons:
- ConvertTo-SecureString basically just prevents you if you expose your file to the public or if an admin gets access to the file.
- You are bound not only to your user but to the combination of a user on a single machine
- If you need it in a automated script you will have a script using this module where you can easily add and save credentials in a way suitable for your situation.
Nevertheless its reasonable secure and quite convenient but there must be a good error handling around it to detect wrong users or computers and give back meaningful error messages.
You could leave out the credential part. This can be implemented by the user if they want to. Main point is to be able to save a session and be able to switch between sessions easily.
Hello All,
I have create a pull request #368 where I prepared changes that should provide a solution for the issue. I appreciate any comment or question.
Hi @timerplayer
I saw your PR and I will add some comments as soon as I have some time - but your PR did not yet pass all the tests