DevOps Bootcamp
Preface
I’ll preface with this post is related to a project I lead at work. I can’t share too many details about it here unfortunately (including code), but what I can safely share is my experience and process π‘.
tl;dr
I lead the development of a medium-size shiny application that is used by users within my organization. This app has evolved to the state where I need to move it from the current platform to a platform that is capable of scaling π and has storage π. How can I re-factor my existing app to integrate with this? Keep in mind, not only the access, but the data access too.π―
First Steps
Until very recently, I had almost no experience with Azure or it’s systems, or it’s esoteric lingo π¬.My first step was to attempt to read test data (prepared by an engineer in a File Container) into my local R. I’ll βοΈ the details of this setup, mostly because I don’t quite understand all that the Azure DevOps EngineersοΈ π¨βπ§ had to configure, but also my scope is restricted to R and shiny in this post.
I looked into the AzureStor
package because by it’s description alone, it’s what I was going to need to do: Access files from Azure Storage. Straight forward right π€·ββοΈ? Not exactly β.
It turns out I needed to use the AzureAuth
package first, whose sole focus is on establishing authentication with services on Azure using Active Directory. OK, so game plan. Use AzureAuth
to “connect”, get a token π, then pass it to functions in AzureStor
to do operations like read/write/list. Once I could do this, I was confident my app would work in it’s entirety. βοΈ
This task in reality took quite a bit of time to map π΅. Remember, I don’t speak Azure. I also don’t expect the DevOps engineers π· to be familiar with this R package nor support it. Enter growing pains π ποΈββοΈ π¨βπ§.
In the end we did get it mapped out, some parameters / settings more obvious than others, and I could finally read Azure File Containers hosted on the platform in my local R instance. π
Here is the function I used:
AzureAuth::get_azure_token(resource, tenant, app, password = NULL, username = NULL, certificate = NULL, auth_type = NULL, aad_host = "https://login.microsoftonline.com/", version = 1, authorize_args = list(), token_args = list(), use_cache = NULL, on_behalf_of = NULL, auth_code = NULL, device_creds = NULL )
Shiny
Oh man, you would think just because you can produce something in a local R session, you can throw it into a shiny app and have it work right? right? Wrong β.. That was a learning here.
I’ll mention too that on this new platform, we’ve chosen to use Posit Connect to host our shiny apps going forward. This product is definitely new to the DevOps team and while I have experience deploying apps to it, I don’t have experience in configuring it. πΆ.
Some weeks later, the connect server was setup, and I could deploy hello world apps to it from my local R instance. I could actually deploy my full app, but it essentially didn’t do anything since I didn’t re-factor the data connectivity. Small victories. π
Hello App, with Azure
Our first task was to setup any kind of hello world shiny app that could read the files stored in the Azure file container. We eventually got this to work by using the app-password (defined in the link above). This was a reassuring sign of being on the right track βοΈ. But what we really needed was to use the user π₯ credentials (since they will be tied to data access within the app). This was a lot more challenging, but an absolute necessity given what my app does.π
I’ll say this vignette was super-helpful in theory, but a lot of the pains β were configurations specific to our companies internal systems π¨βπ»
Hello Matt, with Azure
I tried for the life of me to get the app to pull my credentials with no luck. Relying on the app-password was just a non-starter for this project. Enter AzureGraph
π
We had Microsoft Graph setup in our Azure system. In the end, after many hours of googling (GPT-4 was not helpful here) and experimenting, I was able to get it working locally. Two steps forward, one step back. In the end, I managed to integrate the Graph work flow into a shiny app βοΈ.
This is what I did:
- request a token ποΈ using Microsoft Graph service. This tokeοΈn did contain my user creds
- clone that token for use in another Microsoft Service, namely Azure Storage
- pass that token when listing/reading/writing files in Azure using
AzureStor
within the shiny app βͺοΈ - βοΈβοΈβοΈ
- Profit π€.
It worked.
Below is the culmination of many hours and weeks of learning about Azure, and it’s integration into R and Shiny, with the added complexity of our companies systems/policies. πππ
In the above, I have a simple dashboard with a user card displaying credentials. What’s really neat is that since SSO is enabled, I only really need to click a button to actually get a token using the chain of events defined above. What’s this mean?
<em>User’s don’t really have to “login” anymore π. Even better, because the storage is setup and the ACL maintained, users only see what they are supposed to see. No more manual setups βοΈ …at least from my end and from the perspective of Shiny.</em>
Okay that’s it. I just want to also say I’ve learned so much from working with the Azure folks πͺπ»; really I have π. They were super patient with me, and really took the time to explain micro-concepts (much of which abbreviated here or skipped for brevity).
πHereπ is a nice link to the Azure* Ecosystem of R Packages. All sorts of useful stuff for interacting with Azure, Sharepoint and lots of other services:
Till next time, π»π₯π₯