 Hi there and welcome to the Octa Terraform Workshop. In this video and the accompanying blog post, I'm going to walk you through getting started with using Terraform to manage resources in your Octa developer account. If you like science fiction, you're probably familiar with the concept of terraforming as transforming some inhospitable distant planet into a place that's much more how people want it to be. And similarly, the infrastructure as code tool Terraform can take a description of how you want your cloud infrastructure to be and go out there and make the necessary changes to make the infrastructure match that description. One way I like to think about it is that writing Terraform code is a bit like writing a really good ticket for an old fashioned ox team. The ticket describes what the infrastructure needs to look like once it's complete. Maybe I need a user account with this username, this email address, this employee ID number. If the ops team was doing it by hand, they would go and log into an admin console somewhere and they would first see what changes need to be made in order to complete the ticket. Once they've checked what changes would need to be made, they would then make those changes and click the right buttons to create that user with that email, that name, that ID number. Similarly with Terraform, you would first write the code, that's like writing the ticket. Then you would plan the code to make sure that it's going to need the changes that you expected it to. Then you would apply that code to actually implement the changes that were planned. When an ops team member goes and logs in somewhere and clicks the right buttons, they're using two things. They're using their knowledge of that system and they're also using some credentials that give them access where other people might not have access. Terraform similarly needs knowledge about how to use a given cloud provider and also credentials to get into the particular instance where it needs to make its changes. So as we start writing our Terraform, the first things we will do is we will go get the octa provider so that Terraform knows how to use octa in this project and then we will configure that provider with the credentials to use our octa developer account. First things first, let's make sure we've got Terraform installed correctly. I'll check that on the system I'm using with Terraform-v. This tells me that my current Terraform version is 1.5.3. If this version were out of date at the time that I'm using it, I would see an error under that saying, hey, please click here to download a newer version. So go ahead and check your Terraform version and if it gives you an error that is out of date, pause the video here and go update your Terraform. Next, we'll need to log in to an octa developer account. I happen to already have one so I'll log in but if you need to sign up, now's a good time to do that. I'll log in with Google but you're welcome to set up email if you'd rather. And here's my admin console. We'll notice very first off that I've got my account ID up there in the URL. We'll use that when configuring the Terraform provider in just a minute. Now that we've got a good version of Terraform and we're logged in to our octa developer account, we're ready to create our Terraform project. Our Terraform project will just be an empty directory to start with that we're going to create the file main.tf in. In this directory, we can have as many .tf files as we want and Terraform will also store some stuff of its own in there which I'll show you as we get to it. So empty directory and what do we put in main.tf? Well first we're going to need to tell Terraform to use the octa provider so that it knows how to interact with octa. We can see how to use the octa provider from the octa docs on the Terraform registry. If you ever need to go find this since you only use it once per project, just search for octa Terraform provider and choose the registry.terraform.io So now when we run Terraform init, Terraform will go get the octa provider from octa slash octa on the Terraform registry since we publish it there. But that's not quite enough to control your particular octa developer account to create resources in it. To tell octa which account we're actually using to configure that provider, we will need the provider block which will look like this. Right now we need to go find some fields although we have the values for others. We know that the base URL will be octa.com because on our developer account we see octa.com there in the URL. We also know because of our plans for this workshop that we're going to need the groups.manage, users.manage and policies.manage scopes. If you were doing something else with Terraform you might need different scopes but you can always change these scopes later. The org name is the easiest to find because it's right here before dash admin in the URL. So I'll copy that org name and paste it over here into main.tf. Next we need to create a client that Terraform can use to interact with our octa org. To create our client we're going to go into the sidebar under applications and applications. We're going to create an app integration and it is going to be of the type API services. We'll hit next and we'll name it. I'll call this one Terraform Workshop and hit save. Now we have a Terraform Workshop app. It's got our client ID here and then we'll need to make a private key. First though I'll copy the client ID and put it over here in the provider. The next thing we need to do is provide Terraform with the secrets that it will use to authenticate to octa through this application. Now for you to follow along easily I will show you the secrets that I'm using on my screen but I can just rotate these and destroy the ones you've seen as soon as I finish filming. In your own production use cases the secrets that Terraform uses will be long lived. They will persist for as long as you're using Terraform except for when you rotate them and provide the new ones to Terraform. So handle them very carefully because anyone who has all of the credentials that Terraform is using to act in your octa org can do anything that your Terraform could. This is especially important if you're running your Terraform in automation. Make sure that you're using an appropriate secrets management solution for your needs. With that disclaimer out of the way I'll show you how we get the right kind of key for Terraform to use. First we'll edit the application and switch it over from client secret auth to public key private key auth. Then we'll generate a key pair. We'll let octa generate this key for us. We'll select the PIM version and we'll copy it to our clipboard. We'll then save this as pkcs.pim since it's a key in the pkcs format. So now that we've saved that pkcs.pim key we need to convert it into an RSA key for Terraform to use. Double check the docs if you're watching this long after 2023 because this may have changed. However right now we will run an open SSL command to convert the key. That will go open SSL RSA-in pkcs.pim-out RSA.pim. And now we've got an RSA key. It says end RSA private key at the end instead of just private key. So there's two more things that we need to do. First we need to save the changes that we've made to our octa app. So I'll hit done here and then I'll hit save here. It's okay that we won't be using the client secrets because we're not using client secret auth. Now we've got ourselves a key. Now over in our Terraform we need to tell Terraform to use that key. To do that we will fill out that the private key is going to get path.module slash RSA.pim. That path.module variable says it's in the same folder since we saved it in the same folder that we're doing all our Terraform project work in. So with those things set up Terraform has everything it needs to talk to octa. This tells Terraform that in the current directory RSA.pim is the private key we'll be using. If you have secrets management you can of course put that file onto disk or use environment variables in a way that works with your secrets management solution. So now the octa provider needs knows everything that it needs to take actions in my developer account for me. Now there's one more thing we'll need to do in the octa admin console in order for Terraform to be able to use this integration to take actions and that is we need to grant the scopes for the kinds of things we want it to be allowed to do. So we'll go to octa api scopes and we'll grant the scopes that match the list that we wanted over in our Terraform. I'll grant groups.manage, users.manage and policies.manage. We can see which scopes we've granted of course on the granted tab. Those in octa match those in Terraform. So this looks good and we're ready to continue. Now that main.tf has everything Terraform needs to go find the provider and set it up let's run Terraform init to download that provider. Terraform init is going to go find the provider that we requested. If we'd pinned a version it would get the right version and then it will give us the success message it's successfully initialized. If you got a failure here the first thing to check is that you didn't make any typos in your provider name and that the provider that you specified really does exist out in the registry where you told Terraform to get it from. Now everything is set up and we can create our first user. First let's look at which users we already have in this particular developer account. It's just me in here so far. Then let's check the Terraform provider docs to see what options we have when creating a user. These docs on the Terraform registry will tell you everything you could possibly configure and also which fields are required. So let's create a simple user with just the required fields for now. In this case the user is going to be nicknamed bird. It's going to be the Rufus Hummingbird. I can now save this Terraform and plan to see what will change. The plan tells me that one thing will be added that is the Hummingbird user account and it will have these fields. Those all look right to me. It won't change any existing resources and it won't destroy anything. Since that plan proposes changes that I expected I'll apply it. Apply will show me the current plan in case it's been a while since I planned and have we confirmed that yes I do want this to happen. So when the apply completes we have added one resource. Let's look over in our developer console to see if we see that user account. And there we go. We now have two users. Now you do one. How about you make the Swallowtail butterfly Papillio Zalicaian that's spelled out in the blog post. Pause the video. Go ahead and add another user to your Terraform. What will you do to see what the Terraform would change? What will you do to make those changes? Once you've done that let's see if your Terraform looked how mine would. I would open main.tia and I would create the resource. I'm going to name it Butterfly since it happens to be a butterfly. Papillio Zalicaian with the Swallowtail butterfly at example.com. Then I would save that main.tia, Terraform plan, check the plan and Terraform apply. The plan wants to add one thing which is that butterfly's account. That looks good to me so I'll apply it. The plan and the apply still looks good so I'll tell it yes to create that account. Now when I refresh my users list I'll see the butterfly as well as the hummingbird. Creating a group will look very similar to creating users. First we can see what groups we have so far. Just the defaults. Everyone and Octa administrators. Then we can look up how the Octa Terraform provider wants us to specify things about groups. We can add that group resource to our main.tia. I'm going to call my group the Garden and in that description I'll put the Terraform created it. Of course in your own descriptions put what's helpful to you to know about it. I'll save that and then I'll plan to see what would change. It wants to add one group which is exactly what I'd expect so then I'll apply it. Yes I do want those changes. The apply completed successfully so now when I refresh my list of groups I can see that we've got that garden there. However it doesn't have any users in it because where would we have said who goes in? To do that mapping I will need to describe it in Terraform. Let's see how group memberships work. In the example they create their own group although we already have a group we can see here that it will have a name, it will have which group we're putting the members into and then it'll have a list of members that we're adding. So let's do that to add both the hummingbird and the butterfly to the garden group. As before we'll be putting this in main.tf and I've called this group of the group members of the garden, I've called it garden members. You can call these things whatever you want internally because these nicknames that you give them in Terraform will be useful when you're referring to them later. So when I talk about the bird user I refer to that bird same as up there by its ID I can look that up basically from the resource. That you'll notice the bird butterfly and lowercase garden those don't actually show up in your developer console. So now we've got our group memberships let's save that plan and apply. The plan adds a group membership resource which looks right so I'll apply it. The apply looks good so I'll tell it yes and then it'll make the change. It succeeded. So now if I look at my groups again the garden has two members in it and they are exactly the two members that I added with the group membership. Are you starting to feel more comfortable with this development process? It's a pretty similar loop no matter what you're configuring. First you decide what you want to do then you check the Terraform provider docs to see how to do it then you write the Terraform code, plan it and apply it. However there are a few places that errors can pop up. Let's meet some of those errors on purpose so that if you ever encounter them by accident you'll know exactly how to solve them. One error we might encounter is if we'd use the pkhts key instead of the rsa key. Let's see what that would look like by switching over to point instead of the one that we converted from it in main.tf. What will happen if I try a Terraform plan now? It will quite helpfully say that the rsa private key is of the wrong type. Yeah because it's not an rsa key that we gave it that time. So for now Terraform does need exactly an rsa key so that conversion step is really important. I'll put it back so that we can plan successfully again. When we use the rsa key everything's fine. Another thing that can go wrong with the keys is when you have too many keys in the app that you're using for Terraform. Let's try that and see what kind of error we get. I'll find the Terraform workshop app and then here in its keys I'll add a new one. I'll generate it save it and now we see that there's two keys active. What will happen when we try to plan? It's going to throw errors on every resource that tries to plan about an invalid JWT key ID. To fix this we can deactivate that second key with that key set to inactive the plan should succeed. It's fine now so I'm just going to go ahead and delete that second key to avoid future confusion. Another thing that can easily go wrong is getting the scope slightly off. Remember how the list of scopes in the Terraform provider needs to match the list of scopes that are granted in Okta? Well what would happen if they didn't? We can see that pretty easily by taking one away on either side. I'll do it via Okta. I'll just revoke that users dot manage and then I'll try to create a new user. In this case let's make the fox love flower. So with that user scope gone what error will we see? So we will see that it plans to add the token was missing scopes so let's fix that and create the user. We'll re-grant Okta.users dot manage and then it looks the way it should and we can apply it. So now we have another user. Can you add that user to our group? Go ahead and pause and add that fox love flower to the garden group and then start back up and see if you did it the same way I did. Did you just add that extra line to the group memberships? Let's plan and apply it. This time we'll notice we're not adding anything in the Terraform plan but we are changing one resource. That's because the garden memberships has changed the list. It's not replacing the memberships just modifying it. That looks right so let's apply. So now if we go look at the group we'll see that we've created the flower and put it in the group. We have all three accounts in that garden group just the way that we expected. So that's a lot of setting things up but how about some taking things down? How would you go about removing a user that was created by Terraform? Well it's really quite simple. All you have to do is remove that user from the Terraform configuration. However there's a little gotcha. Let me show you what happens if we just try to remove something. Let's remove the hummingbird since they migrate away for the winter. So what's going to happen if I plan this? I removed just the bird resource but was that enough? The problem is that if you remove a resource you also have to remove every reference to that resource. In this case it's referred to in the group membership and the group membership doesn't know what to do if you tell it to add a user that doesn't exist. So let's fix that by also removing it from the group. Once we remove all references to it we're able to get the plan through. Now we see that it wants to change the group and destroy the hummingbird resource. That's what we expected so let's go ahead and apply. Now if we refresh the group we'll see that we've only got two users in it and if we check our users list we'll see that we've only got the butterfly and the flower. So hummingbird's gone for now. Another way that you can destroy things in Terraform is you can tear down all of the infrastructure that it created and is tracking in its Terraform state file with the Terraform destroy command. This is great when you're prototyping and obviously very dangerous if you have your Terraform working with production. You don't want to remove production resources although it can be very nice to remove all of the changes that you made with your test resources in order to clean up after yourself and test something different. One thing you may notice about the code that we've written so far is that we have a lot of hardcoded strings in it. Some of these obviously should be hardcoded like individual names but others of them might not need to be. For instance the domain that everyone has their email at. Let's use that as an excuse to bring in a Terraform variable and see how that might work. First I'll add the variable to my Terraform file. I'm going to call the variable domain since it's going to be our email domain. It can go anywhere in here. The order doesn't really matter. So this creates the domain. It is of type string and it's going to default to example.com. I can then go through places where I see example.com used and use Terraform's variable syntax to use the domain variable instead of example.com. I'll repeat that process to replace it wherever I want to. So since the default value of the variable is the same as what it was previously hardcoded to, nothing will change if I plan without any arguments. But what if we were switching our email over to a different domain? I could pass a different value for that variable when planning with Terraform. That would look like this. So let's say we're moving from example.com to test.org for some reason. Now overriding that variable everything that uses it will update. If I wanted to apply that change I would similarly pass the var argument to apply. Do you think that you could use a variable to replace that hardcoded list in the group memberships? So far we've done everything in one main.tf file. That's a fine way to develop Terraform while you're just playing with it and trying things out but as your project gets bigger you'll start wanting to split it up into separate Terraform files in the same directory. Terraform will recognize everything in this directory as belonging to the same project. For instance you might want to split out your users into users.tf. Especially if you've copied and pasted some resource definitions from the web or from examples in order to edit them sometimes your whitespace can get pretty badly messed up in your Terraform files and they can be more difficult to read. If that's the case Terraform will let you auto format them with Terraform format spelled fmt. So for instance let's introduce some really bad whitespace into main.tf. Let's just make it ugly and gross. That's much harder to read so let's save that and then make Terraform fix it for us. To do that we'll just run Terraform format. It will tell me which files it changed in this case main.tf and now those problems that I introduced are all gone and it's standardized on the recommended formatting. Another task that you're very likely to encounter especially if you're already using Okta is the question of how do you bring resources that already exist in Okta into being managed by your Terraform? The answer to this is Terraform import. To demonstrate Terraform import we're first going to need a manually created resource that wasn't created through Okta. To do that I'll make a user account of a tree for the garden. So I'll add a person. It will be a user. I'll make acer macro file on the big leaf maple. The username and primary email will be the same there. Everyone is still at example.com because we didn't apply the change that might have swapped them over to test.org. I'll save that and then we can import it into our Terraform. To import this tree user into my Terraform I will need to know its ID. I can get its ID from the URL when I look at it. I'll click on its name and in viewing the profile the string at the very end of the URL is the identifier. To import this manually created user into my Terraform I'll create the file imports.tf and tell Terraform where I'm importing from and where I'm importing to. So I'm importing to an Okta user that will internally be called a tree and the ID will come from the profile of the user. Here's how you find the ID. In your user list you click on the user and then here at the very end of the URL that's the ID that you're looking for. Now Terraform knows where it's coming from that ID in Okta and where it's going. A user that's internally called tree. I'll save that imports tf and then I can plan to look it up. Now that Terraform knows where it's getting the information from and what it's creating with that information we can plan with the generate config out flag. So we're going to say generate config out into generated.tf. So we'll run that plan. We'll take a peek at generated.tf to make sure it seems to be importing the right stuff. Looks like more than I want but the stuff I care about is there and then we'll Terraform apply to bring it into our state. So the apply says we're importing the one resource, that tree. That's correct. That's what I expected. So we've imported that one resource and now we need to factor that a little bit more cleanly into our code. For that refactor I'll copy the parts I care about from generator.tf into main.tf. In this case I've also replaced the hard-coded domain name with the variable that we're using just like in all the other users. I'll save that and remove the imports.tf and generated.tf files since they're no longer needed. Now if I plan we might see a few changes that bring it in line with the defaults of the Terraform provider but those shouldn't be major. I see that it will update, ah I corrected my spelling, that's okay, and it will update it from stage to active. That looks good to me those are changes I want so I will apply it. Now my user has its spelling corrected and is entirely managed by Terraform. The problem that we call configuration drift happens when changes in Terraform diverge from changes in octa so people might be changing things by hand some of the time and through the code other times. The way that you handle this config drift really depends on your organization's needs. Importing like we just did is one way to handle it. If changes made to octa are more accurate and the Terraform tends to lag behind importing might be the right solution for you. The other possibility is that changes made to octa by hand might usually be mistakes and the Terraform might be the source of truth you want. Let's see how we would handle config drift if that's the case. To fix config drift by forcing everything to match Terraform we'll apply the Terraform. Let's see how that might look. Let's manually remove or deactivate one of our users. Maybe the foxglove user since foxgloves are poisonous. I'll find that user and I'll suspend them. I'll also remove them from the garden group so now the Terraform description of the world and the octa admin console have drifted apart. Terraform thinks the foxglove is not suspended and belongs in the group. The console disagrees so to update octa itself to match the Terraform all we need is a Terraform apply. The apply will show me the plan and I'll check that for correctness so I see that it's using the group membership to put the group back how it was and the user account settings to put the user back how it was. If this is what I want I'll say yes and it will apply those changes. Now things are just like they were before I did anything to remove that user. So as you can see Terraform gives you almost unlimited abilities to automate the stuff that you used to do through the admin console by hand. However, automation means not only that you do things the same way every time that you apply it but also that things can happen very fast compared to doing them by hand. And when you do a lot of things very fast in octa via the API you can run into the API rate limit. So I'd like to show you one trick that can help you manage how Terraform uses your available API rate limit. We're going to go find our Terraform app in the admin console and in that app we're going to visit the application rate limits tab. Right now we have various application rate limits for the API and Terraform is only allowed to use up to half of each. Since Terraform is the only app that I have doing things for me via the API in this account right now I might want to edit that and increase it to say you're allowed to use the entire API rate limit Terraform or possibly if I had a variety of other tooling running I might want to stop Terraform before it consumes the entire limit before it even consumes half of the limit and say actually you only get a quarter of those limits so that tuneability can help you keep Terraform from stepping on the toes of the other applications in your org. You can also as you get into more advanced Terraform usage find ways to structure and refactor your Terraform so that you avoid redundant API calls which have some advanced documentation available. If you've followed along with the video this far congratulations you have successfully done most of the basic actions that it takes to manage your octa organization using Terraform. You've set up an app in octa for Terraform to use and configured the Terraform provider to manage octa through that app. You've created users groups and group memberships and you've learned how to create any octa resource that you want by using the provider documentation. You've caused and fixed some common errors and learned how to prevent problems with the API rate limit. You've removed resources as well as creating them and learned how to do simple refactors and formatting of your Terraform code. We've thought a little about config drift what happens when Terraform and octa change in different ways and learned the two ways to address it by either importing resources from octa to Terraform or applying Terraform configurations to put octa back how the Terraform described. Even with all that we've only scratched the surface of what you can do with Terraform and octa. You can build on the skills you've learned today later on to build your own Terraform modules as well as just projects. You can use Terraform to manage the Terraform application itself in octa and you can run your Terraform in continuous integration or continuous delivery software to always keep your octa org in the expected state. So let us know in the comments below what you're doing with octa's Terraform module and which advanced topics you'd like to hear more about.