Using Terraform with Azure

rw-book-cover

welcome in this video what we're going to do is we're going to learn how to create a virtual machine in Azure using terraform you'll see how to create the basics of that virtual machine but also we're going to look at how to make it Dynamic and in addition we're going to look at how you can configure an application to it using cloud in it now we're going to do all of this from tiof 4 it's kind of pretty straightforward I think but if you've never used before

you've never seen it You' never touched it then there is a terraform Basics video which kind of digs into the basics of that workflow and you can see the description of that in the link below but for now assuming everything's good you're ready to go let's have a look and let's start digging in so in terraform when you're interacting with a cloud like Azure what you need to do is you need to add it as a provider so the provider is a is a kind of a a plugin that

interact between the terraform configuration and the apis of azure so on my screen here what I have is the documentation for the Azure provider now in order to add this to terraform what we do is we're going to add this to a terraform block so if just by clicking on that link there you can see there is the block that I I need let's just add that to the configuration and then let's kind of explain what's going on here so I'm just adding this to a file called main.tf here the first thing to notice

is the terraform block and the terraform block is where you can of write which providers your application configuration is using this is not required in terraform but it is good practice because what you're doing is you're pinning the version of a provider so in this instance here hashicorp Azure RM to the version 3116 and why that's important is the terraform provider can change so some of the attributes may change so it's possible that your configuration you

wrote a couple of months ago drifts away from the current version of the provider by pinning the provider what you can do is you can ensure that that change is managed by you when you want to kind of make those changes now one thing I will say I would always recommend you to kind of keep your provider to the latest version you always want it on the latest released version that way all of the latest bugs have been squashed and all of the latest features and resources have been added now the second block here is

provider block now the provider block here allows you to configure certain elements of the Azure provider so let's take a look at some of those options so if I just scroll down here we'll be able to see an example so we have an example there and it's kind of showing you some of some of the options now the the kind of the options you're allowed to configure with well you have this features block which can be used to customize the Behavior now this is a a required block we have to add it but we don't have to put any values in it we we'll can of see that in a second the

the client ID which is the the kind of the client ID for authenticating against the Azure API subscription ID and tenant again those things are required for authentication and then obviously we we have things like the client certificate or the client secret now in addition to being configuring those in the provider block what you can also do is set those with environment variables so for example here for the client secret we can set that to an environment variable arm client secret it's always better to keep

those things external to your terone configuration you don't want to be hardcoding client secrets and details like that inside of your your configuration because if you accidentally leak that if you accidentally commit that to a public GitHub somebody else has got your API credentials that's not going to leave you in a good place but we're going to see how you can do this how you can configure those environment variables how you can keep the credentials safe and secure and and kind of keep everything nice and neat and tidy first things first let's add that features

Block in Tellis sense for the terraform provider is actually saying well hey you know you've got two flu blocks specified you need one block for for terraform and that is the the features block so we can just put features in here as an empty block we don't need to to kind of add anything in there when we're not really interested we're just satisfying the the requirements of the provider there so that's done what we can do now is kind of have a look at that authentication process process so how do we create the

credentials that terraform needs to authenticate with [Applause] a so the environment variables that need to be set so that terraform can use the Azure API are these ones here that I've got on my screen so I need arm client ID I need arm subscription ID ATT tenant ID and arm client secret now you may already have these but you may not I don't it's a way that I authenticate against Azure is that I just log in

through the console Now by logging in through the console I'm just using my username and password but in order to to be able to authenticate as a as a kind of an application and on behalf of terraform we need these four details let's have a look at how we can get those so I've just opened up my Azure console in order to to create the credentials of terraform needs the first thing I need to do is add an application and I do that in the Microsoft entra ID SE C of the console if I scroll down on

the left hand side here you'll see that this is app registrations so by registering an application what I can then do is assign it permissions and then generate the client ID and the client secret the tari form needs so the first thing to do is to create a new registration so I'm just clicking the new registration button here and I'm just going to call this terraform Basics example

and I'm just kind of selecting the the first option so it's it's the kind of this single tenant and I can ignore all of those other things there I'm just hitting register so now that we've created the application what we need to do is assign permissions to it so we do that from the subscription so open up my subscription here my subscription is called terraform test and what I need to do is I need to click on the access control section here now I'm going to click add and I'm going to add a role assignment

now the the kind of the permissions are are very kind of granular so there's there's a lot of different options and and the best thing that you can do when you're creating a role for terraform is to make sure that it only has the permissions that it absolutely needs to keep things nice and simple what I'm going to do is I'm just going to add the the contributor role so this is a privileged role that I'm accessing here from the privileged ad administrator roles Tab and I'm going to select contributor and press pressing next and

then what I'm going to do is assign that to the application that I just created earlier so we're going to select the members we got the little box that pops up here we are going to search for terraform and we are going to find it here there we go so we have terraform Basics example I'm selecting that I'm just going to then press next uh we're going to just make it permanent and then

eventually I'm going to click there review and assign so I've created the application I've assigned the permissions to that application what I now need to do is fetch the credentials for it where do we get those from let's have a look so I'm going to go back to to home and I'm back over to entra ID I'm going to choose my my application so I go back to my my app registrations there and I'm choosing my terraform basics

application now if you see here on the left hand side what we have is certificates and secrets so I'm clicking that and I want to generate a new client secret so new client secret I am just going to set this for 90 days I don't need anything too too big and what you can see there is the client secret that's been being created now you've got a secret ID and a value what I want is the value so I'm just going to grab that

and what I'm going to do is I'm just going to paste that into my one password so this is the arm client secret so where do we get the arm client ID from well we can get that if we go back to our applications if I just click on overview you can see here that we have the the application client ID so we can grab that copy that again to the clipboard and pasting that into one password and that that is my arm client

ID I also need the tenant ID well the tenant ID again I can grab from here I'm just going to to grab that so we have the client ID we have the tenant ID we have the client secret what we need finally is the subscription ID so to order and get the subscription ID I can just click on the subscriptions and you can see there that I have my terraform test and there's my subscription ID so again I'm just going to copy that and I'm just going to paste

that into one password so now I have all of those details I can start populating the environment variables now what I'm doing here is I'm actually going to use one password you don't have to use one password I use it because of convenience I use it because I use it on my desktop machine and because it allows me to keep secrets like my Azure credentials or my AWS or my gcp credentials nice and safe if you want to kind of use this approach then one password has a command line tool which actually allows you to

directly pull the credentials out of one password and automatically inject them into a variable script like I have here and the way that I do that is I just use the say the one password command line now I could just manually paste these into this script here so there's no reason at all that I couldn't just set my export let's have a look arm client ID and then set that to a value so if I grab that from one password and just

grab my client ID and if I paste it into there you know I can set those values like that the thing you've got to remember is if you're setting any form of environment scripts or you're setting anything like that make sure that they're in your get ignore do not check these things into GitHub because somebody will be stealing your resources I've got all of those set so I'm good to go the next thing that we want to look at is how we can go about creating virtual

[Applause] machine so before we go and start creating a virtual machine the first thing we want to do is run terraform init so terraform init is going to download or asure provider and it's going to store it locally so that we can use it so terraform in it running that there it's reaching out to the terraform registry and just downloading and installing the provider now let's have a look at the virtual machine [Applause] configuration so I have the docks here and the resource that we're going to

create is called Azure Linux virtual machine so what this allows me to do is create a VM inside of azure now if I scroll down here I'm going to see an example so the example here is creating a resource Group it will create a virtual Network a subnet a network interface and finally the virtual machine these are all the components that I need in order to be able to create my my VM so let's just copy and paste this EXA sample into our code and

then we can make some slight changes to it all right first things first so the resource Group the resource Group is kind of a container for all of the resources that I'm going to create inside of of azure I'm just going to leave the name as example resources but I will change the name here and I'm going to change this to open web UI so the name that's referenced on the stanza block here is not the name that will be created inside of azure it's the referenceable name that I can use inside of terraform so I'm going to update all

of those other areas of the example I'm just going to change the names of them here and of course I'm also going to have to change the names of the interpolated variables as well let's take a quick look at this actually so when we see a reference like this in in terraform it's quite often called interpolation so the value location there is going to come from the resource Group stanza or the resource Group resource from here now what terraform is

doing by also having these links like this is It's controlling the order of which resources are created so by referencing the location from the resource Group in the virtual Network I can ensure that the resource Group will be created before the virtual Network so I don't have to manually control the order the order of which you put resources in a file in terraform doesn't really matter you can have multiple files across a directory you can have

all your resources and different orders the order is based on what terraform determines it needs to do when it's building a graph of resources and that graph is by those references so let's just finish up this example let's just kind of rename everything here and then we can go ahead and hopefully create that virtual machine so I'm just changing all of the names there open web UI and then we'll take a little bit more of an in-depth look at what's going on all right so that's all the names

changed and this is just from that example that I've just cut and pasted I haven't changed anything else yet let's have a look at what's going on so we're going to create the resource Group a virtual machine needs to be attached to a network so we're going to create a virtual Network now a network requires that there is a subnet so again we're just going to create the the subnet here so our Network's addressed space is is quite broad we're using 1016 and then for the individual subnet where we're kind of

using a much smaller portion of that we're going to create that as 10 02 then what we need to create is a network interface so the network interface is added to the virtual machine the network interface you can see here is referencing the subnet and we're setting that we want a private IP address for this allocated dynamically then we have the virtual machine so the virtual machine well this is going to be created inside of azure and we're going to call it example machine we have to add it to the resource Group and the location and

we're going to look at the size now the size here we're setting is standard F2 let's make this a little bit more efficient we're going to use a smaller virtual machine that's kind of cheaper for doing some tests so what I'm going to do is I'm just going to change this to standard A2 and I'm going to use V2 so the admin username this is the the username that we are going to to use wherever we want to authenticate to the virtual machine I'm going to call this open web UI the network interface that

is the referenc network interface that we created just earlier here and then we're going to add an administrator SSH key now the username needs to be the same so we're just going to change that to open web UI and then the public key path so if you don't have a a private key in a public key part for accessing machines GitHub or anything like that there are tutorials on the internet where you can create this I have MH key already on my machine the public part is

actually in temp so I'm just going to change the location to that and we're going to load that now if you look at this here we're using this function file now I will dive into functions in a little bit more depth later on but what file means is it's just going to load that public key and it's going to assign it to the public key property there then we've got the disk and the source image now the source image here is using auntu 22 two we want to change

this I don't want to use a buntu what I want to use is Debian so let's have a look at how we can reference that Debian [Applause] Im so how do I reference that Source image without having to hardcode any of the values so what I can do is use a data source block now a data source allows me to read information from Azure or from the anything that the provider is connected to and to be able to retrieve those values a data source ins side of the Azure provider we already

have is called the Azure platform image now what this allows me to do is it allows me to search the marketplace catalog for platform images and to be able to return the details so that I can then use that inside of my virtual machine now the example here that we're going to copy is just using Azure RM platform image and you can see that I require the location the publisher the offer and the skew now these details where we can get those from we can find

those inside of the the marketplace but we can also use the Azure CLI to look those up let's see how we can do that for daban 11 so what I'm going to do is I'm just going to run a command on using the the AZ CLI tool so the a CLI I'm using VM image list I'm going to Output to a table and what I want to do is filter on Debian 11 and I want to again all of the latest images so let's see what comes back here so this has output quite a few images we can see that the sort of the dailies there what we want

is a just a standard deban 11 so let's just back scroll up here and we can start seeing that we've got things like Debian 11 got the version and the offer so the offer is Debian 11 the publisher is Debian and the skew is 11 so I just want the latest so we're going to copy this data block here and then let's paste that into our application let me just scroll up to the top here okay so I paste that data element in there and I'm just going to change the name of it to

make it consistent with everything else so we're just going to call it open web UI now the location we've got hardcoded there is Western Europe again we can use that reference so we're going to use the uh Azure RM Resource Group open web UI and then we're going to use the location the publisher well the publisher uh we're going to use from that Marketplace lookup is Debian the offer this is going to be the Debian 11 and the skew what we

want is 11 so what's going to happen is the data element is going to use these details it's going to look up that Market Place image and it's going to fetch me the the most recent version that I can use I can then use those inside my uh my resource so let's let's add that I'm just scrolling down here to my resource and I'm going to change all of these hardcoded values to reference those in a data element so I'm getting the publisher I am going to to get the

uh offer uh the skew and also the version and then all of that's there what we can now do is we can run a terraform plan and a terraform apply to create [Applause] those all right so let's run that terraform plan terraform plan and the plan is going to check my configuration and it's going to work out what changes need to be made so

here it's telling me that there's five resources to add and we can see it's our virtual Network our subnet the resource Group the network interface and you can see here things like the source image reference and that was kind of like we're referencing from our data element the SSH key which we're pulling in using the file function and all of that looks good so let's just on on apply and create those

resources now terraform apply is also going to Output that plan of changes just as a kind of a sanity check I've already seen that so I'm just answering yes and terraform now is using that provider to interface to Azure and it's going and it's creating all of those resources and if we pop over to the Azure console we'll actually be able to see those being created so I'm just going to go over to Resource groups here and you can see here example resources

that we've just kind of created so now currently we've got the example Network which has been created the Nick and then eventually well we can see there the the virtual machine is now being also created so let's go back over to terraform and now it's done we have that virtual machine created all of the other resources have been set up and created inside of azure and everything's good so that configuration that we've just written has now resulted in Cloud resources being created inside of azure

so let's start digging a little bit further the first thing we want to do is to be able to log in to the virtual machine so let's kind of have a look at how we can update some of our configuration to be able to do that now first we briefly mentioned functions so let's have a look at functions a little bit more in depth and see how you can really leverage those kind of make your configuration much more [Applause] Dam so terraform is a concept of a function and a function is something

which is kind of like a a mathematical operator so you can see here the example is using Max and it will give me the max value from this this kind of list of values there are other numeric functions I can get the the absolute value of something I can kind of get the you know FLW something so I can get the the sort of the the major integer part of things string functions which allow me to do string substitution and things like that and then there are more sort specific functions like IP network functions one

of those IP network functions is side a subnet and what side a subnet allows me to do is it allows me to take a subnet and and calculate another subnet from it so if we go back to our configuration we were doing this kind of manually but we can actually do this automatically using a function let's have a look so here in the Azure subnet we're using this address preflex and this is kind of a hard-coded value but we can use a function to replace this so if we

replace this with the cider subnet function so cider subnet and what we can then do is we can specify the the original subnet and that's going to be the open web UI address space now let's have a look at this now what you can see here is it's open UI webspace so this is actually a list so by to get the first value in the list I'm going to use the index positions zero and that's what's using

these square brackets there then what we're going to do is we're going to control the number of bits that we want to add to this subnet so we want a 24 bit so we're going to add eight bits and then we're going to specify the number of the range that we want so the previous value that we had was 10.2.0 sl24 so in order to do that we're going to use the second index and then that will give us the same value so that's going to now give us that subnet

which is 10. 0.2.0 24 which will give us an IP range of 1020 to 102 255 there's many other functions that you can use inside of terraform and and I really recommend spending some time in looking at that documentation because they're incredibly useful you've seen two so far you've seen that file and you've see inside a subnet and we're going to use this more as we kind of progress through the configuration but first things first let's kind of dig in now let's have a

look at how we can authenticate to that machine how we can log into it so in order to log into a virtual machine in Azure it needs to have an a public IP assigned to it so in order to do that what we can do is we can use the public IP resource here so you can see that we are going to Define our public IP we're going to call it open web UI public IP the location in the resource Group we're referencing from our Resource Group and we're doing an allocation method of

static so that's going to create us a public IP inside of azure that we can then assign to a network interface so our network interface here is open web UI and what we can do is we can update this IP configuration and we can specify a public address IP and then we're going to use the Azure um public uh address ID and we can reference that there now we've got that configured so we've got the public I

public address there assigned we're creating the resource for the public IP let's have a look at how we can update some configuration and how teral manages [Applause] that so we've added some resources and we've made some changes to resources let's run a tarform apply and see what happens so what you can see here is it's saying that there's one to add one to change and one to destroy so form is smart enough to understand what already

exists and which resources can be mutated and which resources need to be destroyed completely in order to be recreated so we're going to add that public IP and we're going to update the network interface to assign the public IP and that's all output here in the plan so I'm just going to click yes we're going to make those changes and then we should be able to log into our VM okay so that's done so now we can log into the virtual machine one small

[Applause] problem we don't know what the public IP is so we could go over to the console we could look it up in there but there's actually a smarter way to do it and that is that terraform can actually output those things in the CLI when you define them as an output variable let's have a look at how we can create an output variable for that public IP now I'm adding these to this file here called output. TF but it doesn't matter you could put them anywhere in any file it just helps I think to keep things nice

and clean and separated so let's look at an output variable so an output variable looks like this so we're using this output reference we're giving it the name public IP now it doesn't have a type so it's only the two parts there and then you set the value so the value again is using that interpolated value the value that we're going to use is the Azure RM public IP open web UI which is the name of it and then we're going to get the IP address of it so we add that

and save it let's run terraform apply and then we'll see that public IP output into the CLI and there we go so that was really quick because it didn't have to do anything the the public IP was already in terraforms internal storage so it just output it so we can grab that value now and I can run that SSH command to get it but there's also a better way to do this let's have a look at a little trick that I like to use when I log into an SSH of a machine now to get the output variable I don't

have to run apply what I can do is run terraform output and terraform output is going to list all of those output variables that are specified I can also specify an individual output variable by giving its name so Terra form output public IP so this now is just outputting the public IP the thing that you need to know note here is that it's wrapping this in speech marks and

that's because the type is a string so that terraform doesn't wrap it in speech marks what we can do is you can use the flag raw so we're just using that there flag raw and now you can see terraform is only outputting that public IP so why do I care about this what what what's important about being able to do this well it it shows me a nice little trick so let me just grab this command going to copy this here and I'm going to clear my terminal so I'm going to do sssh and

then I'm going to do the name of the machine the username that I need to connect which is open web UI and then I specify the the at symbol and the IP address that I want to connect to so I could go terraform output and I could copy and paste that IP address or I can actually use a subshell so I'm going to use a subshell here which has my terraform output command just going to press enter you can see there that now the machine

there it's connecting to the host do I want to connect yes I do and there we go we've connected up to our virtual machine everything's been provisioned everything's been created so far so [Applause] good let's now get into the really useful stuff let's see how we can actually provision an application to this virtual machine so that we can do something useful with it first things first let's clean up after oursel now if you remember from the basics video there

is a command called terraform destroy and what terraform destroy is going to do is it's going to reverse any operation that you've done with apply any of the resources that you created with apply destroy is going to remove it's a safe command to run because when we've got this apply command we can always recreate those resources now I have to say you don't want to probably do this against your prod infrastructure destroying production infrastructure is going to leave you having a pretty bad day but

for our little demo application it's a great way to kind of save money and save some time when we're kind of spinning these things up and down again let's just wait for that to finish and that's done so we've got six resources destroyed if we have a look at the console again and then let's just refresh this and everything's gone and in fact if I go back to Resource groups and then just refresh that as well you can see that the the overlying resource Group

has actually been deleted everything that I created with that terraform code with the destroy command has been removed now let's have a look at how we can provision an application and do something useful to that virtual [Applause] machine now in order to provision our application what we're going to do is we're going to use a cloud init script the Azure VM allows us to specify that cloud in it script with this custom data parameter so we're we're going to look at how we create that so we need to

build that cloud init bundle and we could do this manually but it's quite painful so what we're going to do instead is we're going to use another provider we're going to use the cloud init provider so the cloud init provider is actually implements a data source and the data source here is cloud init config it allows us to specify the parts that we need for our Cloud init config such as any scripts the main configuration itself and it will automatically gzip it and B 64 and code the output that we can pass straight to that custom data let's have a look at

how we set that up and let's look at the configuration itself that we're going to use to provision our application so like all providers what I'm going to do is I'm just going to add the cloud init provider there and I've just grabbed that from the documentation so we're going to use cloudinit 2.2 then what we can do is we can create that data element let's scroll up here and we're just going to add this up at the top and we're just going to add our our Cloud init configuration let's have

a look at what this is so we're specifying that we want the bundle to be zipped and we're specifying that we want that to be base 64 encoded which is the requirements for the the custom data parameter inside the Linux virtual machine now we have to add two different parts here the first part is going to be our init script this is going to be the script which is going to install the various software that we need and we also need the cloud config config itself let's have a look at those two files so

the cloud init file we're keeping this really really basic all we're going to do is we're going to run an update and an upgrade and then we're going to run a command the command that we're going to run is going to be this init script now the init script here we're just referencing it from the default location whenever you add a script to a cloud init bundle which we are doing inside of our data Rel so you can see here this part is is defining the init script and it's a shell script so that will be

uploaded to this location here now let's have a look at this in it script itself and this the script itself what I'm doing is again I'm using that file so that that kind of function inside of terraform and I'm referencing provision basic now let's path look at this path do module so this path. module here that we're referencing with this dollar curly bracket what we're doing is we we're interpolating inside of a string and

because we're interpolating inside of a string I've got to wrap the requirement for that variable in this instance it's a special referenceable variable inside of terraform path which G allow gives me the absolute path of my current vm. TF file or the the directory that contains it in fact and then what I can do is I can reference the relative part which is scripts provision BAS basic the total of

this is going to be an absolute path so I don't need to worry about any form of directory changes or anything like that it's going to be calculated dynamically for me so we're loading that into the content let's look at this file itself so provision basic let's see what is going on here so provision basic is just a bash script we're using the set e so to make sure that if any command fails the entire script will fail it won't continue and we're specifying that we want the Debbie in front end to be non- interactive so any appt install that we

do which has any sort of uis or anything like that will will bypassed I'm then just doing an update and I'm installing a couple of packages what I'm installing is caat I'm installing curl squl light and Apache 2 utils and I'll show you why I need those in a little bit then what I'm going to do is I'm just going to set up an install Docker and this all comes from Dockers install instructions so we're just adding the dock gpg key we're adding the app repo

and then what we're going to do is just that update and then we're installing the docker C the C CLI and the various other different bits and pieces that we need in order to run Docker the reason that I want to run Docker is because I'm going to use that because my application that I'm going to run open webui comes packaged as a Docker container this is actually a really nice segue to show you what we're going to do so we're going to use open web UI and what open web UI allows us to do is is it allows us to interface with an llm that might be

running locally or it allows us to interface with an open AI API such as open AIS paid offering now we can kind of use this to create our own environment where we can play around an experiment with with llms where we can also run them locally so that we can do private rag based Integrations but it's just a nice little application to show you how to provision something to a virtual machine it's also very useful I use all the time and I really love this application so big shout out to the team

who created open web UI so as I mentioned earlier what we're going to do is we're going to use the the docker integration for open web UI and we're going to we're going to run this as a Docker container let's have a look back at our configuration script and we'll see what we're going to do there so we've already installed Docker what we're now going to do is we're going to create a folder for our open web UI configuration because across various different restarts we want to be able to assist the configuration and the databases and things like that for open

web UI Docker containers mutable by default but if you map a volume to the host's volume then you can actually store some of those configurations and databases which you need across those various restarts so we're doing that we're going to set this now we need to authenticate to open web UI so what we're going to do is we're going to preconfig a username by default when you start open web UI it will allow you to create that administ strator account so what we're doing here

is we're taking our default password my password I've already mentioned that hardcoding credentials and things like that and your terone configuration is bad we will look at how we make this dynamic in just a moment but we've got that password then we're using the HD password command in order to create that Apache password configuration version of my password which we can then inject into open web UI database the username I'm hardcoding again to admin at demog GS and then what we're going to do is

we're going to first start open web UI that is then going to prepare any of the default files it's going to create the the base authentication database and then we're just going to wait for that to start wait for everything to be reconfigured and then we can stop it cleanup and then what we're actually then going to do is we're going to update the open web UI database using this SQL statement here this is then going to create our Administration user

using the details that we just created previously and then of course we're all good so by default open web UI is using this SQL light database for its all of its configuration this is why we're just running the SQL light command here and all of this information comes from the open web UI documentation finally what we're going to now do is we're just going to clean up that web UI SQL file which has our password in in it and then we're going to create a systemd unit that we can use

to start open web UI whenever the virtual machine start and again very very straightforward standard system D nothing specific to terraform here which is creating that unit we're going to call it open web UI it's going to have a dependency on Docker so it will only start after Docker has started and then the service configuration here is going to just do a Docker run we mapping the default open web UI Port of 8880 to Port

80 we are going to automatically update our rag models which is just an environment configuration and then we're mapping the directory that we created earlier so the one which contains the authentication database and the various other configuration to the disk of the virtual machine I'm going to specify a name for my container and I can use this percentage n which is a special system D configuration which allows me just to get the name of the unit and then we'll see how that works and see that it gets

named exactly the same as the system D service and then all we're going to do is we're going to reload our system D Damon we're going to enable the open webui service and we're going to start it so when the cloud in its script runs this will only run once when the virtual machine is created it's going to install Docker it's going to configure open web UI setting our username and password it's then going to set that system dunit which will then be start started and then everything will be up and running so now let's add that cloud init script

to the virtual machine so let's just pop over to the virtual machines configuration and we're just going to scroll down here to find our VM and then just down at the bottom what we're going to do is we're going to specify custom data and we are going to reference our Cloud config and we're using the rendered property so this is going to give us a gzipped and b64 encoded version of our Cloud init bundle which we pass Master custom data whenever the virtual machine now starts

that cloud init script will run our provision basic script will execute everything will be installed and we're all good previously when we kind of run this the virtual machine came up immediately there was no delays but now because we're installing some software it's going to be a little bit slower so how do we know when it's complete well we don't and this is the key thing that the way that the Azure provider works is that the virtual machine is going to start the second that the cloud in it

script runs the terraform is going to announce that it is being created when in fact hasn't quite finished doing its setup because the Azure API doesn't tell terraform when the cloud and its script is finished so let's have a look at a way that we can kind of delay the output of terraform so that we know that only when the virtual machine is up and when the cloud in its script is running that we're we're good to go so how can we do that how can we Del delay the return of the terraform apply command until we're

confident that our virtual machine is up and running well when the virtual machine application is completed Port 80 will have an application running on it so in a in a kind of a so if I was running locally I could just curl that IP address and I could see that once I get a response and a 200 response that I know my application is up and running from terraform what I'm going to do is I'm going to use a provider and I'm going to use the terill provider that's created by my good friend devops Rob so the Terra curl provider gives you all of

the capabilities of curl but it kind of wraps it up in terraform configuration so I can then use it from inside my config I can do a lot of different things with this but but in essence one of the things that allows me to do is it allows me to continually retry until it gets a success and that's part of those parameters so let let's configure this up let's see how this is going to work so like everything what I do is I'm just going to grab that required provider information I'm going to paste that into

my config so pasting that into the configuration there then I can add the Terra curl block so let's just do that so the Terra curl stanza is going to look like this so what I'm doing is I'm doing a Terra Cur request the name is just open web UI and then the URL that I am going to execute is going to just be a get request and it's going to be on the public IP that we created earlier so once the application is run running this is going to return me a 200 so what I'm

going to check with Terra curl is I'm going to say right I want or I expect a response code of 200 I want you to keep retrying for 120 times at an interval of 10 seconds until you get that 200 so that's basically going to block terraform until it gets that 200 response or the max retry has exceeded and in this instance it's going to be around 20 minutes that we're going to block for it's a really nice way to kind of just check or or to do any API based

configuration Terra for curl obviously supports post and put and any of the other sort of HTTP verbs so it's it's great for this purpose let's run that terraform apply and then let's create those resources so let's first run a terraform init to install Terra curl and then we can run that apply terraform init that's going to go it's going to find that it needs to install Terra curl it's installing the correct version and now we can run and apply which is going to create those resources

for us so we are generating that cloud in configuration we can see that we're now adding seven resources Terra curl and cloud in it and things like that I'm answering yes to that and terraform is now going to go away and it's going to create those now this is going to take a little bit longer than it did before because now we're waiting to provision that application but let's just wait for that to finish and then we'll see what's going on okay so we can see now that that's completed let's just grab that IP

address and then let's log in so that's the open web UI up and running there so the application's now being provisioned it's not particularly useful I don't want to log in just yet because I want to make some slight [Applause] changes one of the things that I want to be able to do is make this Dynamic so I want to be able to make that password automatically generated I want to be able to pass the username in as a dynamic variable so we're going to see how to do that right now there's one more thing that I want to be able to add and that is I want to be able to

configure this application to be able to use an open AI API key so that I can use an open web UI to interface with the open AI API all of that we're going to do by making the configuration Dynamic with variables and how we're going to use the template file capability to be able to generate that cloud in its script in a more Dynamic way and to be able to use those variables so let's dig in and let's see how we can make this work so in order to make configuration for terraform Dynamic we use a concept

called a variable and a variable allows us to modify a value either at runtime or we can set a default value that can be used within the configuration let's have a look at this so let's create a variable for the open web UI admin user so we're going to do this using this variable declaration here and the variable here is variable and we're specifying the name which is open web UI user and a description so the description can can be useful because it can give a hint to what the variable can be used for there's also a lot of

capabilities where we can do things with validation but we're concentrating on the basics so we have the default value and default value is optional I'm setting this to admin at demo. GS which means that terraform is not going to automatically prompt me to specify it if I didn't set a default value then tarform will raise an error and it'll tell me that I need to provide a value for this particular variable let's add some more variables which are going to be for our open AI key and with this

what we're going to do is we're going to specify the openai base so that's the base URL or the openai API and we're just going to set that to the default because that's using the default version for open AI then what we want to be able to do is we want to set the open AI key so the open a key we're not setting a default so how can we set this well we can set a variable in a number of different ways we can do it with the terraform CLI so I

could write a command like this terraform apply and then I could do VAR and I could do open AI e and then I could specify the value of the key I could specify it like that so I can do that on my CLI the other way that I can do it is that I can provide an environment variable which will automatically set a terraform variable let's look at this way and how we can do it so I'm back just looking at my environment script here so what I can do is I can prefix an environment variable

with tfv underscore and then the following part of the environment variable name can be the terraform variable so in this instance I create an environment variable tfv open API key and what that will do is terraform will automatically assign that environment variable to this terraform variable and that happens automatically whenever run terraform apply destroy or

plan again I'm just using one password I've got the key stored in there so I can use my my little command here which just fetches that out and sets that environment variable but that's that's as simple as as is so now we've got those set what we can now do is we can look at how we can configure that template to be dynamic as well so that we can inject those into our Cloud init script so if we just look at the data source for creating our Cloud init config we can see that we're using this file and all file is doing is it's just loading that script from disk so we're

loading provision basic from from script now what I want to be able to do is I want to make this a little bit more Dynamic so I want to inject some variables into this well I can use a different function and the different function that I'm going to use is called template file so let's just change this template file and then what template file takes is two different properties so it's going to take the path of the file that we want and then it takes a a block of a terraform variables that we

can use to kind of make that configuration Dynamic and it'll get interpolated at runtime so we're going to keep this template file here now I'm going to just change this to provision vs and provision vs is just a copy of my basic script that I was using earlier then what I want to do is I want to configure which variables I can use from within the script so let's just add that and what we going to do is we're going to add this block now intellisense has

actually completed this for me but what I'm doing here is I'm going to make the variables open web UI user open AI base and open AI key available to the template file function so template file cannot automatically just read every variable that's inside of terraform you have to specify which ones are available and I do that by creating this object here and by assigning the value Val from my variables to these Keys once I've done that what I can do

is I can then start using these values inside of my configuration so let's have a look at doing that let's go over to our vs and let's start making it Dynamic so let's see how we can use this template file and how we can use those variables I'm just going to scroll down here and I'm going to find my user that's hardcoded and I'm just going to change this and I'm changing this to open web UI and it is user so what I'm doing is I'm using this

dollar curly bracket and this is the replacement Syntax for template file it's going to replace the value of open web UI which I'm setting here and it's going to replace it with the the absolute value so it's kind of that that escape syntax one thing you need to note about this is that this is actually the same syntax as using things like variable substitution inside of bash so earlier on when we were using that variable substitution to inject the

username and the password into our SQL script which configures the the admin user this is actually going to Now fail because template file is going to try and treat this as a an internal replacement so in order to bypass that what we can do is we can just add a d an additional Escape here so we're just escaping that with an additional dollar and now template file is is going to treat that as a bash environment variable and not its own substitution

replacement so that's something that's important it's kind of something might catch you out if you're taking a standard template and you're trying to kind of make it more Dynamic next let's how see how we can use the open AI keys so I'm going to add a new block here and the new block that I'm going to add is like this so you can see that instead of using dollar and curly bracket I'm now using using percentage and curly bracket and this is the template file Syntax for

using conditionals so here I have if open AI key so the variable that I'm passing into the template file open AI key and I'm saying if it's not blank then what I want to do is I want to Echo open AI key and the value into this file here and what this is actually doing is setting up an environment file that system D is going to use so I can dynamically create that environment file and I can write those environment

variables to it I can then use those within my system D job so we're going to do this note here also that we have this end if again that's part of the conditional statement all of that's available in the documentation for template file and you can see how you can also use like loops and things like that inside of there let's just keep going let's have a look at modifying that systemd unit to now use these environment variables so I'm just going to add a new new section here and I'm adding environment file and I'm

specifying the path to this environment file that we're creating there what I also want to be able to do is and I want to be able to use those two variables AI key and AI base and I do that just by specifying them here so open AI key and open AI base what I've done there is I've set that to automatically have the- e so the- e is the docker command which adds

an environment variable to the the docker command we're we're doing it this way so that we can make this fully dynamic because if open AI key is not set then when we run this system D command it'll just completely be fine so if we don't specify value for the open AI key or we set specify a blank value it'll be okay this is not going to cause any errors now there's one last thing that I want to address with this and that was the password so we're still hardcoding the password we could pass the password as a variable

but there's another and even better way to do it and that is to use another terraform provider terraform has got a great thing called the random provider which allows you to do things like Generate random numbers andom course random passwords let's have a look at it so the random provider that you see here has got a number of different resources we have random bytes random ID integer password patch Shuffle string we're going to use random password random password is going to allow us Generate random passwords which have got a particular length they can use special

characters and you can even specify Which special characters you want for your password I'm going to just use this so let's grab that again from the provider block we're just going to grab random and we're going to add that to our configuration so we're just going to add this into our required providers block we're going to save that and then we can now go and create that random password so let's just add that up to the top here just after just after this block here and we're going to add that

and we're going to create random password we're calling it password it's going to be 16 characters in length and we're not going to use any special characters so to use that what we're going to do is we're going to update template file and we're going to add an open uh web UI password and we're setting that to the result of our random password so that's going to be whatever this random password resource generates whenever we run we can set that there so

now we can update our configuration script again our init script to use that so let's just go over here and then let's replace my password with open web UI password now the configuration is fully Dynamic we're injecting the password we're injecting the username and we're also injecting the opena I key and the base one last thing that we need to do

before we run this and that is that we need to add an output variable so that we can actually get that password so let's just do that we're just going to add a new output variable and this one is just going to be the password there now we're specifying this as sensitive to be true and what that means is that whenever you run terraform apply it's not going to put that value out in plain text it's going to only only allow you to get the value if you explicitly State terraform output and then password it's

it's kind of a nice way to make sure that you're not leaking any sensitive output variables in any cicd or any processes like logs or anything that's there and that's all done now we can run terraform apply and we can update our script now before we run terraform apply we've added a new provider so let's run tform in it that's now going to download those missing provider plugins everything's good there now we can run terraform

apply so let's look at this now we've got an error message here and what it's saying is it's saying control keyword HTTP code is not a valid template control keyword let's have a look at that area of our configuration script and let's see what's going on so it's said here that we need to look at line line 47 let's have a look line 47 R in it's script and what we have there you can see that we're using

percentage curly bracket and HTTP code now if you remember earlier on I was talking about the substitution for these usernames and that the template file is using this syntax of dollar curly bracket percentage curly bracket as its own internal replacement mechanisms for either conditional statement or for variable substitution it's saying here that http pcode is not a conditional statement or a loop it's not a a function that it

knows about and that's because we need to replace that here again and we just need to escape it so the curl command uses this uh this template capability of percentage curly bracket that conflicts with template files syntax so just by adding the additional percentage in front there it's going to escape it and then everything will work just fine so now we can run apply again terraform apply we're adding two resources and we're changing or

destroying one resource so we're going to add two resources we're going to destroy let's have a look at what's going on in the plan here so we're adding that random password we are also going to destroy or replace our virtual machine and let's have a look at why so we're going to replace the virtual machine and terraform is telling us that because we're changing the custom data because we're changing the cloud in it

script this is not something that can be updated in place it has to completely destroy the virtual machine and replace it again that's great and then let's scroll up and we can see that also our cloud and it script has changed and this is what's going to happen so terraform is smart enough to understand what can be updated what already exists and what doesn't need to be recreated but also what needs to be destroyed and recreated because because a property has been changed that can't be mutated I'm just going to answer yes to this and we're

going to wait for this to complete and we'll see that new script with our new Dynamic credentials and we'll be able to log in and have a little bit of a play with open web UI so now that's completed let's grab that IP address and let's log into that virtual machine so I'm just going to log in here so it's admin at demo. GS and then the password I need to get from terraform do terraform output password that gives me that password

there let me just copy this and paste it into that open web UI box sign in and we can see everything's working now the models because we've configured that open AI API we can use any of the open AI models so let's use GPT 4 let's have a look here and let's ask GPT about terraform variables what are variables in terraform so it's saying that variables

in terraform are parameters for terraform modules which is which is correct let's ask it if there are any specific types of modules how do I Define a specific type for a terraform variable so what it's now telling me is that whilst we've been defining variables as just plain strings we can actually Define variables as more complex types we can have numbers Boolean list maps and all sorts of

things like that let's have a look at how we can do that let's now take a quick look at how we can make the configuration even a little bit more Dynamic using complex variable types and how we can create a GPU enabled virtual machine so that we can actually run local versions of llms rather than interfacing with [Applause] GPT let's now introduce two new variables and first one's going to be a complex variable so let me just set this up here so we're going to introduce a

new variable called machine now machine is going to be a map so we're going to be able to specify GPU and then we're going to specify the type which is this GPU enabled type inside of azure and then if we have the map key as CPU we can also have the type as the standard A2 that we've been using what I want to be able to do is I want to use a conditional statement inside in my configuration so that if I'm using GPU enabled I can select the right machine

type let's add another variable here and we're going to call this one GPU enabled so whenever I set GPU enabled in my terraform apply configuration I'm going to select one of this these types so let's let's look at that so at the moment what we're doing is we're hardcoding the size for the virtual machine let's see how we can use a conditional statement based on variable Ables so what we're going to do is we're going to say VAR GPU enabled and then

we're going to say that using this this conditional statement here I'm going to say well if GPU is enabled what I want to do is I want to use machine and I want to use the GPU key and I want to specify the type and then if we're not using GPU enabled we're going to use the type from the CPU and because machine is a map in this particular variable type I can actually use the dot notation to access the individual keys and then set the values

there let's now go a little bit further let's add the GPU drivers to our Cloud init script so that we can set up all of that and then to make open web UI be able to use Docker enabled GPU so we're just going to go over to our vas script and we're going to just scroll down here just behind where we were setting the open AI key what we're going to do is we're going to set a a new block here so again I'm using that conditional statement capability with inside of

template file and I'm saying if GPU enabled what I'm going to do is I'm adding a new environment variable GPU flag which enables the GPU for Docker and then I'm also going to download the NVIDIA drivers I'm going to install all of those and I'm also going to add the Nvidia container toolkit and that enables me to use the Nvidia GPU that's attached to this machine with docker so we're adding that there we just need to make a change to our systemd unit and

we're going to add GPU flag this is all good one last thing the GPU driver whenever we're installing that the machine needs to be rebooted so rather than just starting the service here let's just reboot this machine this script is now completed when we're using a GPU enabled virtual machine we're going to install those drivers set the flag we're going to reboot the machine as necessary I do need to add that additional flag to my

template let's just scroll up and add that here and we're just going to say GPU enabled and we're setting that to our variable and then that's all good everything is now correct and set we can now rerun terraform apply we're going to enable GPU by setting the variable GPU enabled to be true and we're going to create ourselves GPU enabled virtual machine this time so terraform apply VAR GPU enabled equals

true and then let's run this so you can see that we're just doing that check again it's going to kind of determine that we we're adding one resource and it's going to destroy one resource let's have a look at this because there's actually a small issue with this configuration something that we need to make a little tweak to you can see that what's being changed is is that the virtual machine is going to be changed now the problem with this is that we are

using the Terra curl resource after the virtual machine has been changed to determine when our application is ready and up and running if we look at the Terra curl request what you can see is that we're not referencing the virtual machine and interpolation but we're actually referencing the public IP what that means is that in terms of the terraform graph the Terra Cur request will be created after the public IP has been created and whenever the public IP changes the teror curl request will also

change and be destroyed and recreated if the virtual machine changes and the public IP doesn't then the Terra curl request won't be changed to deal with this terraform has got some special metadata types that allow you to be able to kind of have those dependent changes and this specific one is called life cycle if you look at the Docks now life cycle is classified as a meta argument now a meta argument is available to every

resource it doesn't matter which provider it's in it's a standard terraform capability so life cycle we can Define things like create before destroy and what that would do is that would create a resource before it destroys the old one if you're replacing a virtual machine sometimes this is the behavior that you would like another option is that we can do things like prevent destroy so we can ensure that you can't accidentally remove a resource and we can specify ignore changes

sometimes we don't want a resource to be destroyed when a dependency changes what we can also do though is we can specify replace triggered by so this means that if a particular resource which is not a direct dependency changes then this resource will also change so our example we want to change our Terra curl resource whenever the virtual machine's ID changes or the virtual machine changes so let's use that let's use replace triggered by and then let's

reference that virtual machine and we'll see that the difference in the plan there so back over to my configuration and I am doing replace triggered by and then what I'm going to specify here is our virtual machine so it's uh Azure Linux virtual machine open web UI so now whenever the open web UI virtual machine changes this Terra curl request will also change let's run that apply again you'll see

that so I'm just going to clear this terraform apply and now what you see is that two resources are going to be added and two are going to be destroyed and those two resources are that Terra curl request and our virtual machine because we added that dependency nine times out of 10 terraform is fine with interpolation but occasionally you will find that you have a dependency which is not direct so our virtual

machine dependency is not directly linked to Terra curl but it is important to have that link there so using the meta arguments is one way to kind of work around this let's just answer yes let's create our virtual machine with a GPU and then let's see how we can use that olama locally okay so let's create that so we're going to do terraform apply VAR we're setting GPU enabled to be true terraform is now going to tell us

that it should be creating and destroying two resources the virtual machine and teror curl which we can see is happening here and this is all correct let's answer yes to this let's wait for this to finish and then let's see what we can do by running local llms on our new GPU enabled virtual machine because we're now using a GPU enabled virtual machine we can actually run

those large language models locally using olama so let's do that let's try and run mistol locally and then let's ask it a question and see how it compares to GPT so what we're going to do is I'm just going to specify the model M and I'm going to pull that from ola.com so this is going to take a couple of seconds to download so mist is now downloaded loed let's select it and give it a go so we're going to choose mistal latest there how do I clean up

terraform resources let's see what mist's got to say about that so mistol is telling me that I can actually run terraform workspace select if I'm using terraform workspaces we haven't covered that in this video but they are it's a useful command and useful tool and it's also suggesting that what I should do is I should run a plan Dash destroy to kind of see what the impact of my action is going to be and of course we undes spry but you know pretty good that's a local

language model that's not using GPT that is using the the latest version of mistal let's run that let's run that terraform destroy let's clean up all of our stuff and then let's have a look at what we've learned today so far so I'm just running that command terraform destroy now I hope you've seen that by configuring your virtual machines and by setting everything up using terraform it actually makes the whole process reproducible I've created and destroyed

VMS a number of different times in this very short demo and it's great because it enables me to kind of save a little bit of money if I just want to use a virtual machine with a GPU attached for a couple of hours I don't have to leave it running for weeks and weeks because it takes me so long to set up and configure all of that software I can just set it up prepare my terraform script create it and when I'm done I destroy it and then we're all good let's have a little summary of what you've learned so

[Applause] far so we've looked at how you register providers with terraform and why it's important to kind of pin that version to make sure that your configuration is matched to the current version of the provider that you're using you make sure that when you make a change it's your control I can guarantee you providers don't change and break that often but the time you find out it has changed and it has broken your configuration would be the time that you need it the most it'll be 3:00 a.m. when you're trying to

kind of get something done to provision some resources it's a good practice to pin versions and to always keep to the latest version that way you're making sure you've got the latest version of the provider the latest functionality and the resources that are available and all of the bugs have being squashed we've also looked at the provider configuration and you've seen that for Azure we need to set certain variables we need the client ID the subscription ID the tenant ID and the client secret and you've seen that we can create those by using the Azure

console to create an app registration and generate that client ID in secret we've looked at variables you've seen that variables can be fairly straightforward they can just be these simple strings but also we can have Boolean values and we can have more complex types like Maps we've also seen how that can be used internally for your applications and how you can use interpolation values and things like that and conditional statements to be able to assign variables and use

them we've looked at templates and we've seen how you can provision an application using cloud in it we've looked at a sort of a very basic script but then also looking at how we can make that more Dynamic by using some of those variables you you've injected in and we've also looked at output variables outputs being able to provide things like Dynamic passwords that we're creating the public I P for our virtual machine and that kind of ties into that core workflow and and how we can use it

to be able to do things like log into the virtual machine I hope this has been useful it's just been a very kind of quick introduction to how to use terraform with Azure there's a lot more resources out there and we will link some of those things down below but now you can find the source code for this in GitHub and then I could of encourage you to kind of play around with that give it a try out do some experimentation maybe provision something different you don't have to use open web UI you can use any application you want but for now this is all for me thanks so much for watching

and I'll see you next time