>> My name is Thomas Mauer. I'm a Cloud Advocate at Microsoft. In today's video we're going
to have a look how you can run scripts like
PowerShell scripts or shell scripts against Azure VMs without having network
connectivity to those VMs. We're going to even have a look
at how you can run these scripts against multiple Azure VMs at
the same time, so stay tuned. [MUSIC] One thing we often see is that, for example, you don't have
network access to a VM. This can be multiple reasons
like you cannot have it from a company perspective where the network setup just
doesn't allow you to, or you need to, for example, troubleshoot one of these
VMs like you, for example, changed an IP address inside
that VM or you just disabled the network adapter or you did some other configuration things
like disabled RDP or SSH. You want to make sure
that you have a way of actually running and
troubleshooting these VMs, as well as building automation
for your virtual machines. We're going to have a
look at the Run command, which is part of the Azure
Compute PowerShell modules, as well as the CLI, as well as REST APIs. The same thing also
available in the portal. Let's have a quick look
here at the portal itself. You can see here, I'm running
a couple of virtual machines. There are some windows
Virtual Machines and Linux virtual machines. If I click, for example, on one of these, you will see that. Again, this virtual machine here is running and you can see some information here
what I'm actually doing. Now what you can also
see here is that this machine here only
has a private IP address. There's no way for me to go over the Internet and actually
connect to that machine. I have a couple of options here now. If I want to work with that machine, I could have a VPN tunnel, I could have Azure express route, I could have a chump post which
I can go through or I could use tools like Azure
Bastion to access that. However, if I don't have that or
something is broken with that VM, I can use some other tricks here. First before I go on, I actually want to show you
that I don't have access to that machine from my computer. Let's just try to ping
this machine here. You will see, obviously since I don't have a VPN setup or anything, I'm not based in the
same virtual network, so I can't reach that machine, I cannot go and access that VM. But I still want to run
some scripts against it or I want to run some
commands against it. Let's have a look, what we have here. The portal already offers you a couple of options
and if you scroll all the way down here to
operations, again, we have tools like guest and host
updates, backup, Azure Bastion, which allows you to do a
remote desktop connection over the web or an SSH connection. But again, if for example, you want to run a script, that's maybe not
really the way to go. Also, if you, for example, shut down your RDP port on the
machine itself or the SSH port, you probably don't have access to it. For that, we have a tool
called Run command. If I go to Run command, you will see here by
default we already offer you a couple of different scripts which you
can use to troubleshoot. For example, you can
go in here and say, I want to set different RDP port. I want to reset the RTP certificates or the general the RTP settings. I can just simply run
a IPConfig to figure out what IP addresses are
configured on that machine. I can enable Windows Update
PowerShell remoting, reset the admin accounts
and much, much more. All the things I might need to actually go in and
troubleshoot that machine. However, if I need to
run something different, I could go and just run
a PowerShell script. In this case, I could click on
this "Run PowerShell Script". I can enter my PowerShell
script, which is here, just a simple get service. Now I can hit "Run" and you can
see here that this will now run that script on that specific VM. Now, the question here is
now, why would I do that? Obviously again, because I
don't have access to it. How does that script actually
goes and access that machine? Well, we have something
called the VM guest agent, which is deployed in every Azure VM, which then goes in and actually
does the communication. We use that for example
for backups scenarios, we use that for monitoring and we can also leverage that
for the Run command. Now we get the results
back and you can see here, I get all the services running in that specific Azure Virtual Machine. Again, we have the
same thing also for Linux machines where you
actually just can run shell scripts and some additional troubleshooting
commands as well. However, I want to show
you something else cool. Now if I want to build a little bit more than that and
obviously I don't want to maybe use the portal all the time, I want to run a script, I want
to build some automation. What I can do here is I
could go, for example, to my local PowerShell Windows
terminal and run their commands. But I can also go here, for example, into Azure Cloud Shell, which allows me to basically already
has installed the Azure CLI, it has Azure PowerShell, and a couple of other things here. Now, let's say I want to run a
script against that machine. First of all, what we do is
we create a script here. Let's do code script.ps1. Now we can write our script. Again to make it
simple, I will again, just stay here with Get-Service
so I can close the editor here. Now, I've script here, which I want to run against that VM. To run that we have a simple command
called invoke.AzVMRunCommand. What we need to do is we need to
define a resource group name. This VM we just saw is basically in that Test Azure VMs resource group, we also obviously need to add
the name of the virtual machine. This one is the azvm001. Then we need to do some
additional things. We need to define what actually
command we want to run. We have commandId, which
is run PowerShell script. Then we want to give this ScriptPath, and this is just a
file I just created. This is like a locally stored script. So this script could be
like if you run that in Azure PowerShell on
your local machine, this will need to be locally stored, in this case, it's stored
into my Azure Cloud Shell. If I now run that command, it will basically do the same
thing as I just did in the portal, but you can see that I
can now leverage this to build a little bit of
automation around it. Now, you can see we
get some output here. It finished running. It
gave me back some output. You can see here that
it's a little bit sorted, that it's not just an
object we get back, we actually get some
text message back. If you run, we get some
value as well as then actually the result of like
Get-Service back as a message here. For example, in this
sense we need to know, so if you want to use
that programmatically, we didn't also need to
obviously work with that and I will show you just how that works. But you get the sense that I
now could actually go out, and run this command
just a couple of times against different machines
to achieve something. I cannot just do a Get command. I could configure actually
something within that machine. Now, if you want to
learn more about this, there is obviously documentation on Microsoft Docs where we actually have documentation how you can
leverage that Commandlet. Again, this is not just
limited to PowerShell. We also have a Azure
CLI command as well as a REST API which you
can leverage for that. Now, what I want to
show you though is, I want to show you how
you could, for example, leverage this, and build some
automation around it so you can run it against multiple Azure VMs. So for that, let me go into
my local VS Code here. What I'm going to do here is, I'm first going to paste
the command I just used. I'm going to use the Invoke command
I just run against my machine. What you can see here, I
already have a script, PS1, and this is the script I'm going
to fire against a couple of different Azure VMs in just a bit. What this command does basically, it goes out and reads a registry key, like the Virtual Machine is
running on a host in Azure, and I can actually get
the name of that host of that Azure host by
checking out the registry. So that is what I'm doing here. Again, I put you that link
into the description. If you want to find out
more about that command, you can actually check
that blog post out. Let me quickly work on this here. What I'm going to do here is, before I start working, let me quickly clean
this up a little bit. So I have Run command. Then I want to split this basically on a little bit
of multiple lines here, so I get some better
views here. Do that. Let me quickly do this. Now, I just basically go here, and format that ScriptPath. It's probably good to
have it like this. Next thing is, again, I can now run this
against my machine. The first thing I
want do is obviously, I want to actually go out and
set a variable subscription ID. So what I can do here is, I can then just enter my subscription where I
want to run it against. I'm not going to do that right now. But again, you could actually
select that for the variable. The next thing I want do is, I want to run that command now
against a couple of different VMs. All the VMs in my case, which I want to run it against, are stored in the
same resource group. What I want to define, I want to have a variable called Resource Group. Then we're going to use
that test-azurevms-rg. Now what we can leverage is, we can leverage that variable here, and just put this in here instead of having to run that manually there. Now, what I also want to do is, obviously I need before I fire that, I want to set the context. I want to select the
right subscription. So what I would do, set subscription, and then I will just go
and say Subscription ID. With that, I would
make sure that I run this command against the VMs
in the right subscription. I just want to make
sure that I do that. Now, the next thing I will do is, I would actually go out and get all the Azure VMs
I want to run it against. Again, now I'm doing this based
on actually a resource group, however you could run just like
a simple counter or whatever, or you could import a CSV file or anything basically to get
the list of Azure VMs. So what I'm going to do here is, I'm just going to do my Azure VMs, and what I want to do
is, I want to Get-AcVm. Then I need to define the
ResourceGroupName where the VMs are in. Again, I already have
a variable here, so I can get that. Let me quickly run this
here first, so set this, and then I can, for example, just run and list all the VMs
within that resource group. I'm going to run this here, and in the Terminal here, you will see here all the VMs listed. We have all the VMs, the six VMs you saw earlier
in the Azure portal. However, I want to filter that, because it could be that one of
these VMs are, for example, down. So what I want to add is, I want to see the status. So I have a Status switch here, so I want to get the
status of these VMs. You can see here the
difference now if I run this, what you will see here is, that I also see if the
VM is actually running. Currently they're all
in running state. But however, it could be that the
VM is not running, in that case, I don't want to send that AcVm run command against that VM because it would just throw an error. What I'm going to do here is, I'm going to select Where-Object. Then basically say, Okay. I want to see if the PowerState is equal VM running. So what I can do here, this should give me
the exact same output. Let's have a look at this,
and if everything worked, I still get my six VMs, because everything we
have done is running, so that is great. The next thing I want to do, I also want to make sure
that this script now, obviously is running
against Windows VMs. In this sense, I'm just
going to hit "Add," and then I'm going to do StorageProfile, I'm going to select OsDisk, and then I'm going to go to OsType, and that should be equal Windows. If I run this here, and quickly select this, I can just basically go out, and again, I should see all of them, because again, all of
these are Windows VMs. If there will be a Linux VM now in that specific resource group, basically, don't run that against it. Now, I have my VMs here. The next thing I want do is actually, I want to run that now, this command here
against all of the VMs. So what I'm going to do here is, I'm going to use that
variable or this VM object, and I'm going to do a ForEach-Object, and then here I actually
want to run that command. So let's put this in here. I can now run that command. It would now go around
all these different VMs, it would then run that
same script against it. Let me quickly run this
to show you what that actually would do to
these virtual machines. I'm still running that command. At the moment, I'm not
doing any of the ForEach. You can see here, I only
selected that specific command. I'm running that
against one of the VMs, but you will see the
output in just a bit. You can see here that I now got
some information back here. Again, I use some video editing to actually make
this a little bit faster. When you run this script, we will see it will
take awhile actually until you get the value back because it needs some time
to actually connect to Azure and then provide
that to the VM, and from there, you
get the message back. That will take awhile.
But what do you can see here on the message, you can see here I get
the information back. It says "HostName", and
then I get the host name of the Hyper-V host or
the Azure host basically, where this machine is running
on, which is pretty cool. But again, you could run any other command to get some
information out of that yam, or even configure something
within that machine. What I want to do now
here is obviously, I want to define that this is now running in
actually in that ForEach. So what I'm going to say here, I don't want to run it all the
time against the same machine, so what I'm going to do here is
just going to use my name here. So this will then use
the name of the VM, which is in that loop. Now, what I also want
to do here is actually, I want to go and format
the output a little bit. I want to save this here
in a variable because I don't necessarily want all
that information here, which I have around this. For example, I don't want to
know that this succeeded, and so on; I just care
about this message. So what I'm going to do here, I'm going to configure out, and then I create an output. This is what I actually want. Let's say "Output",
and then let's say, I also want to run this
against multiple machines, so I want to make
sure that I know from which machine that output comes from, so I'm going to use ".Name" again. This will then just output
the name of this VM, and then I'm going to do a space, and then I actually want to have the output of the message I get back. I'm going to do that,
and I'm using "Value", and then this is the 0, and then I'm going to do "Message". That will then just
basically put that out, and I want to then
basically get this back, so I'm going to output that. That is now what I
actually would run. I would actually go out and run this now against multiple machines. What will happen is, it will then go and start
with the first machine, and as the first machine is done, it will run that against
the second machine, against the third machine, and so on. As you can imagine, this will take some time to complete. What I can do now with
PowerShell 7 actually is I can run this ForEach-Object in parallel. This now allows me actually to
run that command in parallel. I want to make a nice code here. I can run that, and it
will actually go out and run it for the first machine, and the second machine,
and the third machine, all at the same time. The first thing I want
to do is obviously, run the resource group. Again, you can go out, and if you have multiple subscription in your Azure account,
in your Azure tenant, you probably want to set here
the Azure subscription ID, and then obviously, set that as the context you're
running your commands on. I highly recommend doing that. I already did it in my environment. The next thing I want to do here is, I'm going to run this command to
actually get all the Azure VMs, which are in that resource group, which are running, and which
are Windows, in that case. I'm going to run this, and we also do a quick check here. So if I do "MyAzureVMs", I have a list of these VMs. I want to run now a ForEach here. One thing I need to change
though is, obviously, because I want to use
the resource group, so what I'm going to use here, instead of this, I'm going to
enter the resource group name. I'm going to use whatever is
given from that VM object. Now, I can actually run this command. This will take awhile again
until everything has started, but it will then provide me with all the information from
all the different server, so I will see all the host
names of the Azure VMs, or where they running on. Now, you can see here, I get the results back
almost at the same time. Some hosts are a little bit
faster in responding than others. You can see here, I
get for the azvm001, 002, 004, 003, and 005, and I think it's really working
at 006 to get these results back. However, this is obviously using a feature which came
with PowerShell version 7, and you can use it with
PowerShell version 7 and higher. With the parallel parameter
for ForEach-Object, you can actually go out and run it against all the VMs you have then. Now, I highly recommend there's
also a throttle limit you can set. If you have 1,000 VMs or whatever, you probably don't want to
run it against all 1,000 at the same time because you could
also hit some limitations there, so I recommend that you set the throttle limit there
to, let's say, for example, 20 VMs, or 10 VMs, or whatever is suitable
for you and your scenario. Then you get all these
information back. You can see here, I get all the
information from these back, and I get them nicely formatted. I can easily now see what
I get back actually, from which host, or from
which VM, in that sense. I hope this video was helpful. If you like that video and you
want to see more, like that video, subscribe, and if you
have any questions, feel free to leave a comment. I will put more links and more information in
the description below, and hopefully, see
you in the next one. [MUSIC]