Quantcast
Channel: Hey, Scripting Guy! Blog
Viewing all 3333 articles
Browse latest View live

PowerTip: Find available virtual machine size by using PowerShell

$
0
0

Summary: Use the Azure Resource Manager cmdlets to get the proper name for a virtual machine size.

Hey, Scripting Guy! Question Is there a way to see the proper name for a virtual machine size without creating a virtual machine first?

Hey, Scripting Guy! Answer Just use the Get-AzureRMVMSize cmdlet, and filter on the Name property. You just need to supply a location first. In this example, we are looking at available sizes in the ‘eastus’ location that have ‘S2’ in the name.

Get-AzureRMVMSize –location ‘eastus’ | Where-Object { $_.Name –match ‘S2’ }

The Doctor


Create Azure Resource Manager virtual machines by using PowerShell – Part 2

$
0
0

Summary: Use the Azure Resource Manager cmdlets to define the operating system disk on a storage blob for a virtual machine.

Hey, Scripting Guy! Question Would you help me, please?  I need to know how to define storage for my virtual machine in Azure Resource Manager by using PowerShell.

Hey, Scripting Guy! Answer Honorary Scripting Guy, Sean Kearney, is here as we happily go along our scripting way in Azure Resource Manager. Yesterday, we started the process by defining a virtual machine (VM) configuration. Today, we’ll set up the needed storage for this machine.

The cmdlet in charge of all of this is Set-AzureRMVMOSDisk. It’s called that because there is actually one to define a data disk, too. This is incredibly important when you define a virtual machine because it does help you define the roles in advance.

As an example, it’s always a good practice to separate your data from the operating system. Data will have different needs. Perhaps data needs more speed on far more expensive disks. Perhaps data is not critical, for example, when you’re using a ripper to convert raw data for graphics conversions. After the files are done, you don’t need to retain the temporary data.

In either case, we need to define the disks that are being attached and, ideally, their purpose.

To define a basic operating system disk, we need to provide three parameters:

  • a filename
  • the type of caching that it will need
  • the storage blob to store it on

The file name is pretty easy, of course. You should choose a name that’s unique to the VM so that you don’t clash with other VHD files. You should also consider the possibility that the name that you choose might have been used before. (What? Nobody ever called a VM “Test”?)

To have a simple method to avoid a clash with other files, why not add some details to the file name that should make it a bit more unique, such as:

  • Your initials
  • A department code
  • Today’s date and time
  • The purpose of the disk (operating system or data)
  • The VM name

I’m not suggesting all of these, but in today’s example, we’ll use at least the machine name and our initials to start and follow that by adding the type of disk (operating system or data).

If you remember from yesterday, we defined the VM name in the following manner:

$VMName=’HSG-Server1’

From this point, let’s identify our initials and the type of disk, and build out the file name:

$Initials=’HSG’
$Disk=’OS’
$Diskname=$VMName+’_’+’$Initials+’_’+$Disk

We next need to define the caching option for this disk. If you remember, when we grabbed the VM object from last week, we used it to obtain information that we needed.

The easiest way that I’ve found to get the type of caching for the disk is to reference one from a previous VM. This is stored under the StorageProfile property. You can access it in the following manner, and we’ll use last week’s VM as the example:

$VM=Get-AzureRMVM –name HSG-Linux1 –ResourceGroupName HSG-AzureRG
$VM.StorageProfile.OSDisk.Caching

As last week, we can pipe that into the clipboard or copy / paste the information or just type it:

$Caching = 'ReadWrite'

Our next challenge is to actually put this into the storage blob in question. If you wanted to use an existing blob that another VM was on, we would use last week’s technique by scooping the name from the original VM object:

$VM.StorageProfile.OSDisk.vhd.uri

From that point, you could do a little splitting and substring manipulation. The other approach is to identify a storage blob that you’d like to use with the Get-AzureRMStorageAccount cmdlet. We can list all available storage accounts by just running this cmdlet.

If you’d like to show only the ones that are available for a particular resource group, you can supply the particular resource group as a parameter:

Get-AzureRMStorageAccount –ResourceGroupName HSG-AzureRG

Screenshot that that shows result when you add a resource group as a parameter to Get-AzureRMStorageAccount to return a specific storage account.

Now, if you need to filter on this because you could have multiple accounts, you’ll need to run this through a Where-Object. We can filter on the StorageAccountName property. In the following example, we are looking for storage accounts with the letters, hsg, in the name:

Get-AzureRmStorageAccount -ResourceGroupName HSG-AzureRG | Where { $_.StorageAccountName -match 'hsg' }

After we have a storage account object, we need to grab the blob URI from it. This is a part of the PrimaryEndpoints property, which we can access in the following manner:

$Storage=Get-AzureRmStorageAccount -ResourceGroupName HSG-AzureRG | Where { $_.StorageAccountName -match 'hsg' }
$StorageGroupURL=$Storage.PrimaryEndpoints.Blob+’/vhds/’

You’ll notice that we added ‘vhds’ to the name. That’s because, when we create a VM, it’s where Azure Resource Manager expects the data to be. In this manner, it matches the source URI from last week.

We now pull all the pieces together and assemble the object for the disk. Just as yesterday, it doesn’t actually do anything to Azure yet. We’re just building an object that will be used to eventually attach to a cmdlet to spin up the VM:

$DiskURI=$StorageAccountURL+$DiskName+’.vhd’
$AzureVM = Set-AzureRmVMOSDisk -VM $AzureVM -VhdUri $DiskURI -name $DiskName -CreateOption fromImage -Caching $Caching

At this point, the next part of the journey has been completed. Tomorrow, we start wiring up a network card, virtual, of course, on this machine.

I invite you to follow the Scripting Guys on Twitter and Facebook. If you have any questions, send email to them at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow.

Until then, always remember that with Great PowerShell comes Great Responsibility.

Sean Kearney
Honorary Scripting Guy
Cloud and Datacenter Management MVP

PowerTip: Get the time and date as a string of numbers by using PowerShell

$
0
0

Summary: Use the –format option in Get-Date to change the output.

Hey, Scripting Guy! Question I’d like to build some log files and have the date and time as part of the name. Is there a way to show the date and time in a format where it’s all numbers?

Hey, Scripting Guy! Answer Absolutely! Just use the –format option with Get-Date and supply a format for the output. To see the format as month, day, year, hour, minutes, and seconds, use this example:

Get-Date –format ‘MM_dd_yyyy-HH_MM_ss’

The Doctor

Create Azure Resource Manager virtual machines by using PowerShell – Part 3

$
0
0

Summary: Use the Azure Resource Manager cmdlets to create and attach a virtual network card to a virtual machine.

Hey, Scripting Guy! Question I’m trying to find some guidance about how to use PowerShell to create a virtual network card. Would you be so kind as to lend me a hand?

Hey, Scripting Guy! Answer Honorary Scripting Guy, Sean Kearney, is here today continuing forth (not C++) on our quest to create a virtual machine (VM) with the Azure Resource Manager cmdlets. Today, we’re going to work on putting together a network card.

Let’s think back to a few weeks ago when we created some prerequisites for our environment, namely a virtual network and a network security group. These two pieces gave us the equivalent of:

  • A VLAN or network wire system that we can attach to and communicate with (virtual network)
  • A firewall or the ability to control traffic from said network (network security group)

What our VM will need is a network card (really a virtual one). A great improvement in Azure Resource Manager is that you can create and attach multiple network cards.

Each of these virtual NICs can be attached to a separate virtual network, too, just as if you had a system with one leg on the perimeter network and a separate card on a vendor network.

Last week, we discovered that it wasn’t tricky to get the network security group or the virtual network in use by a VM’s network cards.

We also need to be aware that we can ask Azure Resource Manager those questions as well. To obtain available network security groups, we can use the Get-AzureRMNetworkSecurityGroup cmdlet.

In our case, we know of the security group because we created one a few weeks ago. We also did a neat little trick last Friday to get the information from a VM that was already using one.

$NSG=’HSG-NetworkSecurityGroup’

The other piece that we will need, of course, is the virtual network that we should attach to. Again, we could just grab the settings from an existing VM. However, it’s far more useful to see what’s available for you to consume.

The Get-AzureRMVirtualNetwork cmdlet will dump all this onto the screen. Of course, the default output won’t be all that useful. You’ll find there are three properties that you’ll be interested in. They are Name, AddressSpace, and Subnets:

Get-AzureRMVirtualNetwork | Select-Object Name, AddressSpace, Subnets

Screenshot that shows results from the Get-AzureRMVirtualNetwork cmdlet to return the name, address space, and subnet.

AddressSpace will need to have its property expanded to show the really useful information, which is the network space (192.168.0.0 or 10.0.0.0 etc.) that it’s actually a part of. Subnets from this view will show you the name and little else. Here, we need to do a little magic with Select-Object.

Get-AzureRmVirtualNetwork | Select-Object Name, @{Name='AddressSpace'; Expression={ $_.AddressSpace.AddressPrefixes}}

This will pull out the value of AddressSpace so that we can read it properly:

Screenshot that shows the value of the AddressSpace property.

We’ll also need to do some similar work to access the information from the Subnets property. If you take a quick look at the property, you’ll note that not only do we have the name, but we also have the value for each Subnet. An Id, which we’ll need to acquire, is also attached to each Subnet.

Screenshot that shows the value of the Subnet property.

Accessing these will be similar to pulling out the extra information from the AddressSpace. There’s a little more “Expression” magic, of course, as we acquire the additional properties.

Get-AzureRmVirtualNetwork | Select-Object Name, @{Name='SubnetName'; Expression={ $_.Subnets.Name}},@{Name='SubnetAddressPrefix';Expression={$_.Subnets.AddressPrefix}}

After we have all the information, we can have an easier time visualizing the virtual networks that we’ll need to use and the Subnets to choose. We can tie it all together with the following line in PowerShell. We’re just tying up all three custom expressions into one:

Get-AzureRmVirtualNetwork | Select-Object Name, @{Name='AddressSpace'; Expression={ $_.AddressSpace.AddressPrefixes}}, @{Name='SubnetName'; Expression={ $_.Subnets.Name}},@{Name='SubnetAddressPrefix';Expression={$_.Subnets.AddressPrefix}}, @{Name='SubnetId';Expression={$_.Subnets.Id}}

Screenshot that shows the information for the Name, AddressSpace, and Subnet properties.

After we have identified the network and, more importantly, the Subnet that we need, we just need the Subnet ID. We can get this with the following bit of PowerShell:

$SubnetList=Get-AzureRmVirtualNetwork | Select-Object Name, @{Name='AddressSpace'; Expression={ $_.AddressSpace.AddressPrefixes}}, @{Name='SubnetName'; Expression={ $_.Subnets.Name}},@{Name='SubnetAddressPrefix';Expression={$_.Subnets.AddressPrefix}}, @{Name='SubnetId';Expression={$_.Subnets.Id}}

$SubnetID=($SubnetList | Where { $_.SubnetAddressPrefix –eq ’10.0.0.0/24’ }).SubnetID

Excellent. So, we now have a way to view the various network names and security groups. Now for the fun part: creating the network card for the VM.

For this, we will be using three cmdlets.

The first is New-AzureRMPublicIPAddress, which gives us a publicly accessible Internet IP that’s bound to our VM. If you need, you can remove it later after post configuration if the VM does not need direct remote access.

This object needs to be given a name, and the name has to be unique to the VM in question. For our examples, we will use the VM name that we selected from Monday as the identifying marker.

In addition, the public IP will need a label, and it must be in all lowercase. A tolower() method can be used for this:

$Nic=$VMName+’_Nic1’
$RGName=’HSG-AzureRG’
$Location=’eastus’
$PublicIP = New-AzureRmPublicIpAddress -ResourceGroupName $RGName -Name $Nic -Location $Location -AllocationMethod Dynamic -DomainNameLabel $VMName.ToLower()

Our next task is to attach a network card to this VM. This is done with a combination of two cmdlets. New-AzureRMNetworkInterface needs to be used to create the object for the VM.

# Add Network card

$NIC=New-AzureRMNetworkInterface –Force –Name $VMName –ResourceGroupName $RGName –Location $Location –SubnetID $Subnet.ID –PublicIPAddress $PublicIP.ID

Afterwards, we just add the newly created network object to the VM in Azure by using the Add-AzureRMVMNetworkInterface cmdlet.

$AzureVM = Add-AzureRmVMNetworkInterface -VM $AzureVM -Id $NIC.Id

Now, we’ve completed one of the most important pieces, the ability for our VM to communicate with other systems.

Tomorrow? We choose an operating system!

I invite you to follow the Scripting Guys on Twitter and Facebook. If you have any questions, send email to them at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow.

Until then always remember that with Great PowerShell comes Great Responsibility.

Sean Kearney
Honorary Scripting Guy
Cloud and Datacenter Management MVP

PowerTip: Change current Azure Resource Manager subscription by using PowerShell

$
0
0

Summary: Use the Azure Resource Manager cmdlets to alter the current active subscription in use.

Hey, Scripting Guy! Question I have about a dozen different subscriptions in my Azure Resource Manager account. How can I change it from the default one to one of my others?

Hey, Scripting Guy! Answer You can do this very task by using the Select-AzureRMSubscription cmdlet and providing either the name of the subscription or the SubscriptionId (which is probably more accurate). Here are examples:

Select-AzureRMSubscription –SubscriptionName ‘My MSDN Subscription’

*or*

Select-AzureRMSubscription –SubscriptionID ‘11111111-1111-1111-1111-111111111111’

The Doctor

Create Azure Resource Manager virtual machines by using PowerShell – Part 4

$
0
0

Summary: Use the Azure Resource Manager cmdlets to select the operating system image for a virtual machine.

Hey, Scripting Guy! Question I’ve got most of that virtual machine (VM) assembled, but I was having quite a time trying to pull together the operating system image with PowerShell. Nudge me in the right direction, could you?

Hey, Scripting Guy! Answer Honorary Scripting Guy, Sean Kearney, is here today to get you a little more information about how to build your own Azure VM within the Resource Manager. Our next big piece is to add in the operating system.

But, before I get much further, today is my son’s birthday. So for a few blinking moments, allow me this opportunity to wish him a Very Happy Birthday. Happy Birthday, Son!

When you worked with earlier versions of the PowerShell cmdlets for Azure, you would just run the Get-AzureVMImage cmdlet. This one cmdlet would list every single image that’s available in the gallery.

The problem here is that with 979 images at last count, it would be a massive pause to get them all. Furthermore, consider that you might only want the Microsoft or Linux images? You still had to pull down this large list and then filter it.

When we created out first Linus VM, we pulled out the properties of the VM in the following manner:

$VM=Get-AzureRMVMImage –name HSG-Linux1 –resourcegroupname HSG-AzureRG

$Publisher=$VM.StorageProfile.ImageReference.Publisher

$Offer=$VM.StorageProfile.ImageReference.Offer

$Sku=$VM.StorageProfile.ImageReference.Sku

$Version=$VM.StorageProfile.ImageReference.Version

These are far more manageable collections because the images themselves are now categorized. These four categories are:

  • PublisherName
  • Offer
  • Sku
  • Version

PublisherName

This property contains a descriptive name for the vendor that holds images. However, it’s not as obvious as “Microsoft” or “Linux” in many cases. This is where I find creating a VM first helps. It contains all these properties that I can filter out.

You can obtain the complete list by using Get-AzureRMVMImagePublisher. Because the list of images is different among locations, you’ll need to provide a location.

Get-AzureRMVMImagePublisher –location ‘eastus’

In the case of our Linux VM, the publisher is ‘Canonical’. Yes, I don’t quite get that one either, That’s why I personally find creating a VM first and pulling its properties far easier.

Offer

In most cases, this will typically be the base name of the operating system, such as “Ubuntu Server” or “Windows Server”. I can, for example, use an additional cmdlet if I supply the PublisherName to list all available offers. That cmdlet is Get-AzureRMVMImageOffer, and it requires both a publisher and location.

Get-AzureRMVMImageOffer –location ‘eastus’ –PublisherName ‘Canonical’

From our last VM, the actual offer name was ‘UbuntuServer’.

Sku

When you target a particular PublisherName and Offer, you’ll next target a SKU. Think of the SKU as a refinement of the list, such as Server 2012 R2, Datacenter Edition, or Standard Edition.

To get a list of available SKUs, you’ll need to supply the PublisherName, Offer, and Location to the Get-AzureRMVMImageSKU cmdlet.

Get-AzureRMVMImageSKU –location ‘eastus’ –PublisherName ‘Canonical’ –offer ‘UbuntuServer’

What you’ll notice each time is that the list is smaller and quicker to pull down. Also, as long as you know the particular breakdown, it’s easy to switch a VM script by just supplying these first three pieces. It’s also much faster now to pull down the list of images.

Version

This last property is closer to what you might think of as the patch level. To identify available versions, you need to supply all the first three properties to the Get-AzureRMVMImage cmdlet. This will give you an identified list of images from which you’ll need to select the version.

Here’s where the VM won’t help you much. When you pull its version property, you’ll often see this value, Latest.

But if you try to target that against a “Where-object”, you’ll never get it. If a version is not supplied, we can sort the images against the Version object. In the following example, we pull all the available images and target the “Latest” or newest in the following manner.

$Version=(Get-AzureRMVMImage –location ‘eastus’ –PublisherName ‘Canonical’ –Offer ‘UbuntuServer’ –skus ’14.04.4-LTS’ | Sort-object Version)[-1].Version

After we have all of this information, the trick is to add it to a VM image. This is done through the use of the Set-AzureRMVMSoureImage cmdlet. If you have all of these four properties, you can supply them in the following manner:

In the following example, we add them to our existing VM image object:

$Publisher=’Canonical’

$Offer=’UbuntuServer’

$Sku=’ 14.04.4-LTS’

$Version=’ 14.04.201605160’

$AzureVM = Set-AzureRmVMSourceImage -VM $AzureVM -PublisherName $Publisher -Offer $Offer -Skus $Skus -Version $VMImage.Version

Using the same process from last week, I spun up a Server 2016 image from MSDN. Here is an example of the same image properties from a Microsoft environment:

$Publisher=’MicrosoftWindowsServer’

$Offer=’WindowsServer’

$Sku=’ Windows-Server-Technical-Preview’

$Version=’ 5.0.20160420’

$AzureVM = Set-AzureRmVMSourceImage -VM $AzureVM -PublisherName $Publisher -Offer $Offer -Skus $Sku -Version $VMImage.Version

At this point, there is only one small remaining piece. For that, return tomorrow.

I invite you to follow the Scripting Guys on Twitter and Facebook. If you have any questions, send email to them at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow.

Until then, always remember that with Great PowerShell comes Great Responsibility.

Sean Kearney
Honorary Scripting Guy
Cloud and Datacenter Management MVP

PowerTip: Search Azure Resource Manager image publishers by using PowerShell

$
0
0

Summary: Use the Azure Resource Manager cmdlets to search publishers for a match.

Hey, Scripting Guy! Question Could you show me an example of how to search through the list of publishers for Azure images?

Hey, Scripting Guy! Answer Just use the Get-AzureRMVMImagePublisher cmdlet, and filter on the PublisherName property. The following example searches for all publishers that contain the name, Windows.

Get-AzureRMVMImagePublisher –location ‘eastus’ | Where-object { $_.PublisherName –like ‘*Windows*’ }

The Doctor

Create Azure Resource Manager virtual machines by using PowerShell – Part 5

$
0
0

Summary: Use the Azure Resource Manager cmdlets to assign credentials and create the virtual machine.

Hey, Scripting Guy! Question Now that I’ve built all the objects for the virtual machine (VM), what’s left before we spin up the machine in Azure Resource Manager?

Hey, Scripting Guy! Answer Honorary Scripting Guy, Sean Kearney, is here to finish up our three-week session about VMs in Azure Resource Manager using PowerShell.

Let’s consider everything that we’ve seen over the past few weeks on a high level.

  • Create the necessary resources and infrastructure for Azure VMs
  • Spin up a VM and access its properties by using PowerShell
  • Identify the necessary properties to create a new VM

Then, finally, over the last few days, we’ve been putting together that VM.

Our last step is actually pretty simple, but it’s one of the more important steps. We need to assign the primary UserId and password for the VM. There are, of course, some important and critical details.

You obviously cannot use a UserId of ‘root’ in the Linux environment or ‘Administrator’ in the Windows world as they are both built in. In the case of Windows, this account would be disabled by default.

Now an important note on the password. It can’t be anything obvious. In fact, if you try, Azure will reject it. So ‘Password’,’P@ssw0rd’ are actually right off the list. Won’t even let you, so don’t try.

Well, you can try but, it’s only useful if you like red error messages. 😉

The next cmdlet is Set-AzureVMOperatingSystem, and its task is to identify the key pieces for our operating system.

  • The host name to assign to the operating system
  • The primary user name
  • The password
  • The operating system type

The Hostname (or NetBIOS name in the Windows world) is typically the same as the VM name. It can be unique, but you’re better off to avoid digging in the cloud for the actual name. Let’s just say, this is a good practice to, at least, have them match.

So, remember earlier that we defined our VM name with the following line:

$VMName=’HSG-Server1’

We’ll just use this value for the computer name. To assign the credentials (the UserID and password), we need to create a PSCredential object. This is the same process that we use when we create credentials for PowerShell normally.

You can choose the interactive method (which is a lower risk on security) in the following manner:

$Credential=Get-Credential

You could prepopulate the UserID as well and just pass this to the Get-Credential cmdlet:

$UserID=’HSGAdmin’
$Credential=Get-Credential –credential $UserID

The third option is to fully automate it. In a normal fully exposed PowerShell script, there is high level of risk because the password is in clear text. However, if you are using Systems Center Orchestrator or Azure Automation, you can mitigate this risk by storing the credentials as a secure asset.

$UserID=’HSGAdmin'
$Password='NotSoSecureP@ssw0rd'
$SecurePassword=Convertto-SecureString $Password –asplaintext -force
$Credential=New-Object System.Management.Automation.PSCredential ($UserID,$SecurePassword)

We now define the settings for our operating system by using Set-AzureRMVMOperatingSystem:

$AzureVM = Set-AzureRmVMOperatingSystem -VM $AzureVM -Windows -ComputerName $VMname -Credential $cred -Windows

If this were a Linux operating system, we would simply change the –Windows switch to a –Linux switch.

After we have defined all the particular objects, we need to call up only one cmdlet to do all the magic. We need to supply only three parameters: our ResourceGroupName, Location, and VMimage object that we have been building.

$RGName=’HSG-AzureRG
$Location=’eastus’
New-AzureRMVM –ResourceGroupName $RGName –location $Location –VM $AzureVM

We’ll let this run through and, given some appropriate time, we’ll have a new VM spun up in Azure!

I hope the information that we’ve provided will make it easier to create VMs in Azure. Azure really offers some amazing features outside of just infrastructure as a service. The amazing piece as I dig into it is that almost everything can be accessed and modified by using Windows PowerShell!

Please check it all out! Hope to share more with you soon!

If you’d like a sample of the PowerShell scripts that were used in this series of blog posts, you can download them from the TechNet Script Repository.

I invite you to follow the Scripting Guys on Twitter and Facebook. If you have any questions, send email to them at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow.

Until then, always remember that with Great PowerShell comes Great Responsibility.

Sean Kearney
Honorary Scripting Guy
Cloud and Datacenter Management MVP


PowerTip: Export Azure resource group as JSON by using PowerShell

$
0
0

Summary: Use the Azure Resource Manager cmdlets to export a resource group as a JSON configuration.

Hey, Scripting Guy! Question I’ve heard that JSON is used to define features in Azure Resource Manager. Is there a way to export a resource group in JSON format?

Hey, Scripting Guy! Answer Just use the Export-AzureRMResourceGroup cmdlet and provide the resource group name. It will automatically save a JSON file that matches the resource group name in your current folder. Here is an example:

Export-AzureRMResourceGroup –resourcegroupname HSG-AzureRG

The Doctor

Build a hexadecimal clock in PowerShell – Part 1

$
0
0

Honorary Scripting Guy, Sean Kearney, is here today, and I’m going to sit down and have some fun this week.

Today I was feeling a bit bored and, for some reason, the thought “hexadecimal clock” entered my head. Don’t ask why because I’m not quite sure about the “Why” part. But, it just did.

Then, I sat down and thought, what’s involved? Really?

As a script, from a pseudo-code standpoint, it’s pretty simple

  • Get current time
  • Obtain hours, minutes, and seconds
  • Convert each item to a two-character hexadecimal number
  • Write each character to the screen
  • Loop over and over and over and over

Only I wanted this to be…well…b-i-g. In my head, I wanted a clock as wide as my console. That started my head spinning. “Big letters. How can I draw big letters and numbers?”

In the “old world” before the Internet, before Snapchat, before 10 Megabyte hard drives, there were those of us that lived in the world of Commodore and Atari. Some of us were geeky enough to draw custom fonts in an 8-bit by 8-bit box.

That was my world at one point. I thought it was pretty neat.

In the PowerShell world, I sat down and thought about it. I could easily make the same thing in the console.

So, I began to draw a zero on the screen using nothing but the zero character like the following example:

 000000
00   000
00  0 00
00 00 00
00 0  00
000   00
000000

Feeling pretty pleased with my most excellent artwork, I began to think about how to store it. “An Array! I’ll make this character an array!”

My first thought was to create a multi-dimensional array to access the individuals rows and characters:

$HexArray=New-Object ‘object[,]’ 7,17

Then, for each character in the array. I would do something like this:

$Hexarray[0,0]=’ 000000 ‘
$Hexarray[1,0]=’00   000’
$Hexarray[2,0]=’00  0 00’
$Hexarray[3,0]=’00 00 00’
$Hexarray[4,0]=’00 0  00’
$Hexarray[5,0]=’000   00’
$Hexarray[6,0]=’ 000000’

I would continue with something like this for each character, changing the final position with a 1 and then 2.

But, a little voice said to me, “Too ugly, too complex.” It really wasn’t a hard solution but visually, I wanted to just “Edit the characters without working around quotes and defined objects.”

In this situation, a Here-String would be perfect. I could define a single array and have each member be a single string that’s defined as a Here-String.

First step, define an array to hold 18 characters, one for each of the 16 Hexadecimal characters and two for an optional character like a hyphen or separator for the numbers.

[array]$HexArray=@(‘ ‘) *18

Now at this point, I can simply define each of the characters. I decided on a simply sequence: a number match for each hex character in its sequence from 0 to 15 (0 to F).

To define the characters, it began to look like this:

$HexArray[0]=@’
000000
00   000
00  0 00
00 00 00
00 0  00
000   00
000000
‘@
$HexArray[1]=@’
11
1111
11 11
11
11
11
11111111

‘@

This process continued until I had the various characters defined. With this done, I could now pull up the hexadecimal character ‘f’ by using its numeric position. When done, I would get an oversized novelty version like this!

Screenshot of the hexadecimal character ‘f’ by using its numeric position.

Now the brain began to churn. How could I get the time to mix into this? For that, return for tomorrow’s Hey Scripting Guy episode!

I invite you to follow the Scripting Guys on Twitter and Facebook. If you have any questions, send email to them at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow.

Until then, always remember that with Great PowerShell comes Great Responsibility.

Sean Kearney
Honorary Scripting Guy
Cloud and Datacenter Management MVP

PowerTip: Extend a string array in PowerShell

$
0
0

Summary: Use the built in features of PowerShell to easily add new Element to a string array.

Hey, Scripting Guy! Question I created an array of strings in PowerShell, but I need to add to it. Is there a simple way to do this?

Hey, Scripting Guy! Answer Absolutely. Just use += to add an element to your string array. In the following example, we add a new element to the array that’s named $HSG

$HSG+=’New String Element’

The Doctor

Build a hexadecimal clock in PowerShell – Part 2

$
0
0

Summary: Manipulate string data from Get-Date in PowerShell.

Honorary Scripting Guy, Sean Kearney, is here today to have a little more fun with our silly project to build a hexadecimal clock in the PowerShell console.

Well, after all, who said scripting wasn’t allowed to be fun? That’s usually how I learn, by playing about!

Yesterday, we defined a cool array of Here-Strings that contain all the characters that we’d like to display in our clock. Today, we’re going to access the properties of the current time and see how we could put that to use.

The very first thing that we’ll need to do is pull up the date and time and store that away in an object.

$Now=Get-Date

Good. We’ve got the current date and time. If you run Get-Member against this, you’ll see some very useful properties, namely, hour, minute, and second.

Screenshot of results of Get-Member which returns hour, minute, and second among other properties.

We can access any one of these objects by just connecting the dots. No, literally!

$Now.Hour
$Now.Minute
$Now.Second

This is perfect! We can easily access the data we need. Now, all we need to do is get each one as a hexadecimal number.

To do this, we just need to use this [Convert] accelerator. Converting a single number to hexadecimal in PowerShell is easy. Here, we convert 42 to Hex:

[Convert]::tostring(42,16)

If you really want to be nerdy, you could try Octal by changing the 16 to an 8 for…“Base 8”.

[Convert]::tostring(42,8)

So, for each number, we’ll need to convert the information to a hex digit like so:

$HexHour=[Convert]::tostring($Now.Hour,16)
$HexMinute=[Convert]::tostring($Now.Minute,16)
$HexSecond=[Convert]::tostring($Now.Second,16)

But, we have one extra problem. When I’m done, I want to have two digits for every column. I want to have this evenly spaced and have a consistent number of characters.

A built-in method that we can use for strings is padleft(), which allows us to specify how many characters the output should have at minimum and what and where to “pad the blanks with”.

The padleft method requires two properties: the number of characters and just what you’re going to pad it with.

We will make sure that each position has two (2) characters and will always have at least a single ‘0’ preceding it. It (padleft) will look like this:

Padleft(2,’0’)

So, when we pull up the time and convert hours, minutes, and seconds to hex, it will look like this:

$HexHour=[Convert]::tostring($Now.Hour,16).padleft(2,’0’)
$HexMinute=[Convert]::tostring($Now.Minute,16).padleft(2,’0’)
$HexSecond=[Convert]::tostring($Now.Second,16).padleft(2,’0’)

For the final time piece, we’ll wrap it all together as a single string and make this a basic function:

Function Get-HexTime()

{

$Now=Get-Date
$hour=[Convert]::tostring(($now.Hour),16).padleft(2,'0')
$Minute=[Convert]::tostring(($now.Minute),16).padleft(2,'0')
$Second=[Convert]::tostring(($now.Second),16).padleft(2,'0')

Return ("$Hour-$Minute-$Second")

}

Getting the time in hex is very easy now. We just execute the Get-HexTime function.

Screenshot of the Get-HexTime function.

Swing on over to the Scripting Guys tomorrow when we start to play with how to parse this data and access the array!

I invite you to follow the Scripting Guys on Twitter and Facebook. If you have any questions, send email to them at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow.

Until then always remember that with Great PowerShell comes Great Responsibility.

Sean Kearney
Honorary Scripting Guy
Cloud and Datacenter Management MVP

PowerTip: Use PowerShell to identify a leap year

$
0
0

Summary: Use PowerShell and Math to identify a leap year.

Hey, Scripting Guy! Question Is there a way to use PowerShell to identify whether this year is a leap year?

Hey, Scripting Guy! Answer Actually there is! You can use a combination of Get-date to find out the year and the Modulus function in PowerShell.

((Get-Date).year)%4

If the value is zero, the current year is a leap year!

The Doctor

Build a hexadecimal clock in PowerShell – Part 3

$
0
0

Summary: Use Select-String in PowerShell to identify data and match it to an array.

Honorary Scripting Guy, Sean Kearney, is here today to play more with our funky, nerdy, cool hexadecimal clock.

Yesterday we built out a small function named Get-HexTime to give us the current time that displays hex digits instead of decimal for hours, minutes, and seconds.

One of our challenges is going to be matching up the individual hex digits to our positions in the array. We can step through the list of characters in HexTime with a pretty simple loop:

$Hextime=Get-Hextime
For ($Count=0; $Count -lt ($HexTime.Length); $Count++)
{
$Character=$HexTime[$count]
}

With this loop in place, I just need to figure out what the characters were and maybe convert them to a numeric value to access the various elements of the array.

At first, I thought to myself, I’ll just find out what the ASCII character is by using the following:

[byte][char]’0’
[byte][char]’a’

This process reveals the ASCII code of the character. The numeric characters would be 0 to 9 (and in sequence) with values of 48 to 57.

The letters, a – f, would be 65 to 71. Of course, additional characters would have to be trapped for. My initial thoughts were that I could use the PowerShell switch statement and have it return the matching value in the following manner:

switch ($Character=$HexTime[$count])
{

'0' { $HexValue=0 }
'1' { $HexValue=1 }

...

'a' { $HexValue=10 }

...

'f' { $HexValue=15 }
}

You get the idea. But, first off, I hate to type repetitive information. Then I thought on a different line. I could take the sequence and pipe it into Select-String to find a match.

In the following example, I can have Select-String look for the ‘e’ and return an object that contains the exact position it was found in.

‘0123456789abcdef’ | Select-String -Pattern 'e'

Although the result seems to only be the same original string, if you pipe this to Get-Member, you’ll see some different properties. More importantly, you see a property named “Matches”.

Screenshot that shows the Matches property among other properties.

If you access this property directly, you’ll see something really cool, a deeper property named Index, which is the exact position in the string where this value was found.

Screenshot that shows the Index property.

Because each character in our string is unique in both value and position, we can use this to match against our array.

Now here is how we could pull out the location of the value in the array:

# Get the Character from the String
$Character=$Hextime[$Count]

# Find what position it exists in
$Match=('0123456789abcdef/-' | Select-String –pattern $Character)

# Get that exact value
$Index=$Match.Matches.Index

To make it simpler, we could wrap it all up in one line like this:

$Index=('0123456789abcdef/-' | Select-String –pattern ($Hextime[$Count])).matches.index

Now that we’ve gotten all the hard work out of the way, tomorrow we’ll really have fun as we start to draw this output onto the screen!

I invite you to follow the Scripting Guys on Twitter and Facebook. If you have any questions, send email to them at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow.

Until then always remember that with Great PowerShell comes Great Responsibility.

Sean Kearney
Honorary Scripting Guy
Cloud and Datacenter Management MVP

PowerTip: Set a default value in a switch statement

$
0
0

Summary: Use the default field in switch statement to have an assumed value.

Hey, Scripting Guy! Question I was playing with Select-String and would like to have it default to a value if none was trapped for. How can I do that?

Hey, Scripting Guy! Answer Just use the Default keyword, and you can have an assumed value in the script block. In the following example, the switch is listening for the $DogSeesSquirrel object. If no values match, it returns ‘Squirrel runs free’.

switch ($dogseesquirrel)

{
'Back door' { $Response=’Bark!’}
'Couch'     { $Response=’Growl a bit.’}
'Food bowl' { $Response=’Bark! Bark! Bark!’}

Default    { $Response=’Squirrel runs free’ }
}

My apologies to our little dog for this example. 😉

The Doctor


Build a hexadecimal clock in PowerShell – Part 4

$
0
0

Summary: Use PowerShell to navigate the neutral zone.

Honorary Scripting Guy, Sean Kearney, is here today to continue forth (not COBOL nor even Visual BASIC) into our silly yet fun quest to build a hexadecimal clock by using the PowerShell console.

It does seem silly, doesn’t it? Yet, consider some of the things that we’ve learned in PowerShell during our quest.

  • Here-String
  • Defining array
  • Select-String searches
  • Convert number bases
  • Singing songs about huge tracts of land… (Oh no, wait…. Sorry… That was a movie I watched last night.)

So, although the quest seems silly, we have learned quite a bit.

Actually, it’s much like any quest. The goal isn’t the point. It’s what you learn or experience while getting there.

Our next part is to figure out how to draw this silly thing. Oh, well, good grief!

I hadn’t really thought this one out.

So far, we have a string of characters (Our HexTime).

From this string, we need to identify the members of our array and show them (Giant Letters from Mars).

That part seems simple enough. We can just loop through and show the output.

For ($Count=0; $Count -lt ($HexTime.Length); $Count++)

{

$Character=($HexArray[('0123456789abcdef/-' | Select-String -pattern ($HexTime[$Count])).Matches.Index])

$Character

}

This is a variant on yesterday’s exercise, but it goes one step further. Access the array from the Index value and show the relevant character. This actually works if you want a vertical clock.

I really want a horizontal clock, like the one on my desk. To achieve this in the console, I need to figure a way to move the cursor to a unique position on the screen.

We can use the $host variable, which is a live object in the PowerShell console that contains information such as the color of the screen and cursor position.

With this object, we can see, capture, and, most importantly, set the position of the cursor in the console.

Neat, eh? Let’s clear the screen and capture the current location.

Clear-Host
$Position=$host.ui.RawUI.CursorPosition

If you look at the current object, you’ll see the following results:

Screenshot of the cursor position.

This tells us that the cursor is at column 0 and row 0 (the very top) after we just wiped the screen.

Now, to modify where the cursor is going to be, we do three things.

  • Capture the position
  • Set the X and/or Y values in the captured object
  • Set the position from the captured object

An example of this in action by using Windows PowerShell (followed by Write-Host of some output) can be seen in the following example:

$CursorPosition=$Host.UI.Rawui.CursorPosition
# Set column to 20
$CursorPosition.X=20
# Set row to 15
$CursorPosition.Y=15
# Set Cursor Position
$Host.UI.RawUI.CursorPosition=$CursorPosition

If we’d like to have each of these characters at a unique position, we’ll need to pick a start point and then adjust the column. My Here-Strings are at least eight characters wide, so I’d like to adjust the column by 10 each time. Here’s a basic loop that will:

  • Set the start position
  • Adjust the column by 10 each time
  • Place a character from the array on the screen

# Clear the Screen
Clear-Host
$StartPosition=$host.ui.RawUI.CursorPosition

# Set the Home Position for the clock
$StartPosition.Y=3
$StartPosition.X=4

# Remember where we started
$CurrentPosition=$StartPosition
For ($Count=0; $Count -lt ($HexTime.Length); $Count++)
{

# Set the cursor location
$Host.Ui.RawUI.CursorPosition=$CurrentPosition

$Character=($HexArray[('0123456789abcdef/-' |`
Select-String -pattern ($HexTime[$Count])).Matches.Index])
$Character

# Bump up the counter for the Column and reset the row
$CurrentPosition.X = $CurrentPosition.X+10
$CurrentPosition.Y = $StartPosition.Y
}

But when we run through the loop, we get this result:

Screenshot of results from running through the loop.

My first row of each character is placed correctly, but the rest seem to end up on the same spot. Why is that?

The trick is that with each Here-String is a character that forces the cursor back to column 0 for the additional rows.

If only there was a way to place each row of the Here-String and set the cursor position. We’ll go into that way tomorrow, of course!

I invite you to follow the Scripting Guys on Twitter and Facebook. If you have any questions, send email to them at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow.

Until then always remember that with Great PowerShell comes Great Responsibility.

Sean Kearney
Honorary Scripting Guy
Cloud and Datacenter Management MVP

PowerTip: Set the color of the progress bar with PowerShell

$
0
0

Summary: Use the $Host object to alter the color of the progress bar in PowerShell.

Hey, Scripting Guy! Question I was curious if there’s a way to change the color of the progress bar in PowerShell. Do you know how this could be done?

Hey, Scripting Guy! Answer Glad you asked! You can do this by altering the values for ProgressBackGroundColor and ProgressForegroundColor under $Host.PrivateData.

To change to a bright green background with a black foreground, use the following code in PowerShell:

$Host.PrivateData.ProgressBackgroundColor=’Green’
$Host.PrivateData.ProgressForegroundColor=’Black’

The Doctor

Build a hexadecimal clock in PowerShell – Part 5

$
0
0

Summary: Learn to split data in a Here-String and get a console beep.

Honorary Scripting Guy, Sean Kearney, is here with our final day this week to wrap up building a hexadecimal clock in PowerShell.

When we finished yesterday, we ran across a snag in our output. Most of our rows kept dropping to the immediate left of the screen as in the following image:

Screenshot of badly formatted rows.

For this, we will look at one of our Here-Strings to try and figure out the problem:

$HexArray[8]=@'

 888888

88    88

88    88

 888888

88    88

88    88

 888888

'@

If we look at a Here-String, you’ll notice it is literally a long string. It has an array count of 1 but a large length.

Screenshot that shows the count and length values of the array.

The question is, how can I access the individual rows? If we can identify the character at the end of a line, we could feed that to a split() method. This will turn this Here-String temporarily into an array.

We can run through a Here-String with this small loop in PowerShell to examine and view its values. The [byte][char] converts the letter or character to the ASCII number.

for ($x=0; $x -lt $HexArray[8].Length; $x++)
{


$Char=$HexArray[8][$x]
Write-Host $Char,([byte][char]$Char)

}

The output will look something like this, and please note the repeating patterns of 13 and 10 on the screen.

Screenshot that shows ASCII values for characters at the end of lines.

We can send error Character 13 (Carriage Return) or Character 10 (Linefeed) in to split this into an array. We’ll use the line, Linefeed, as our split character.

$CharacterArray=$HexArray[8].split([char][byte]10)

Now if we examine this, we have an array of data that we can step through.

$CharacterArray.Count

Now that the data is split into an array, we can step through this row-by-row and set the column each time. Here is our original loop that’s adjusted to split the Here-String and then output the data.

For ($Count=0; $Count -lt ($HexTime.Length); $Count++)
{

$Character=($HexArray[('0123456789abcdef/-' |`
Select-String -pattern ($HexTime[$Count])).Matches.Index])
$CharacterArray=$Character.split([char][byte]10)

# Loop through all members of the particular Here-String
For ($Row=0; $Row -lt $CharacterArray.count;$Row++)
{


# Position the Cursor
$Host.UI.Rawui.CursorPosition=$CurrentPosition

# Access the Value and print it
$CharacterArray[$Row]

# Move up the cursor to the next row
$CurrentPosition.Y=$CurrentPosition.Y+1

    }


# Bump up the counter for the Column and reset the row
$CurrentPosition.X = $CurrentPosition.X+10
$CurrentPosition.Y = $StartPosition.Y


}

As it stands, we just drew a single row on the screen. To make this continue, we’ll need to wrap this whole operation in a do loop. One problem with this approach is that there will be a lot of flickering on the screen. What we really want is to update the clock each second that it changes.

For this, we’ll add in a simple trap for the time. We’ll remember the last time that it changed.

do
{


$HexTime=Get-HexTime


} Until ($Hextime -ne $LastHexTime)

Then, at the end of the loop, we’ll drop in a simple line like this:

$LastHexTime = $Hextime

One more fun thing. Wouldn’t it be cool to have a little “Tic” sound as each second passes by? (Well, I thought it would, or at least a neat way to irritate my coworkers 😉 )

We can do this with a simple beep on the console. I tweaked it down to a very short time and actually got a “Tic” sound.

[console]::beep(1000,5)

Wonder what it all looks like? You can download this script from the TechNet Script Repository.

That’s all there is to turning your PowerShell console into a hexadecimal clock! Thanks for sharing the time with us!

I invite you to follow the Scripting Guys on Twitter and Facebook. If you have any questions, send email to them at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow.

Until then always remember that with Great PowerShell comes Great Responsibility.

Sean Kearney
Honorary Scripting Guy
Cloud and Datacenter Management MVP

PowerTip: Remove trailing space from a string by using PowerShell

$
0
0

Summary: Use the trim() method to remove leading and trailing spaces from a string.

Hey, Scripting Guy! Question Could you do me a huge favor and show me how to get rid of spaces before and after a string in PowerShell?

Hey, Scripting Guy! Answer No problem at all. All you need to do is apply the trim() method to remove all the output. You can also use trimstart() to remove the start or trimend() to remove the end. Here is an example:

$Data=’           Look at all of this wasted space on my screen.
$Data
$Data.trim()
$Data.trimstart()
$Data.trimend()

The Doctor

Use Windows PowerShell to search for files

$
0
0

Summary: Use Get-Childitem to search the files system with PowerShell.

Hey, Scripting Guy! Question I saved a file somewhere on my computer and can’t find it. Is there a way to use Windows PowerShell to find it?

Hey, Scripting Guy! Answer Honorary Scripting Guy, Sean Kearney, is here today to show you a cool trick I use all the time. I use PowerShell to search for things constantly!

Why PowerShell? Well, to be honest, I have a bad, bad, bad habit of putting data where it shouldn’t be. Sometimes I’d use locations that the Indexer in Windows isn’t watching. In these situations, even Cortana can’t help me.

We can use Get-Childitem to show a list of files and/or directories quite easily. The following example lists all files on the root of Drive C:

Get-Childitem –Path C:\

If we add a –Recurse parameter, we can show everything that we have access to.

Get-Childitem –Path C:\ -Recurse

So far, this is no different from running the following command in the CMD prompt.

Dir C:\*.* /s

So, why PowerShell?

For searching, PowerShell offers us all the things that we don’t see under the hood. In that folder structure, there are bound to be many files that I cannot access. Those will throw an error (red by default) and makes it very hard to read.

So, we tell PowerShell, “Don’t bother showing me those minor errors, just continue.”

Get-Childitem –Path C:\ -Recurse -ErrorAction SilentlyContinue

But, how do we use this as a search tool? Get-Childitem includes two additional parameters, -include and –exclude . Their functions are pretty simple.

The -include parameter says, “Show me only these files in the search,” and -exclude says, “Keep that stuff out of my way.”

As the person on TV used to say, “But wait! There’s more!”. Sometimes the reason you can’t find a file is because it was stored in the Temporary Outlook folder.

That used to drive me bananas! Because Temporary is a hidden folder, you often will miss that, and so will Get-Childitem. To bypass those issues, add the –force parameter to let it examine those folders as well.

Get-Childitem –Path C:\ -Recurse –force -ErrorAction SilentlyContinue

We could now use this same command to show only the Word documents that I can access or maybe even all the files that I put the letters, HSG, in. Yes, odds are that I was watching too much Babylon 5 and stored a file in my Pictures folder.

Get-Childitem –Path C:\ -Include *HSG* -Recurse -ErrorAction SilentlyContinue

Unfortunately, it pulls up everything, and I mean everything. with the letters, HSG, in it, including folder titles. We can tell it to show only files by using PowerShell. This was introduced in version 3 of PowerShell.

Get-Childitem –Path C:\ -Include *HSG* -File -Recurse -ErrorAction SilentlyContinue

We can also use the the -Exclude parameter to say, “Don’t show me any TMP, MP3, or JPG files.:

Get-Childitem –Path C:\ -Include *HSG* -Exclude *.JPG,*.MP3,*.TMP -File -Recurse -ErrorAction SilentlyContinue

Here is where things really get powerful. Let’s get right down to the nitty gritty. Imagine that it was a really long weekend.

You did the work on Friday. You even forgot the file name! Just how do you sort that out?

In PowerShell, we can filter out files based upon date and time quite easily.

Right now, let’s get the date for June 24, 2016. I’m not planning on this being a long weekend but, hey, I could be sick on Monday so, it’s possible 😉

There are fancier ways of doing this, but I’m going to use Get-Date because it works internationally. First, supply the year, month, and day.

$FindDate=Get-Date -Year 2016 -Month 06 -Day 24

With this information, I can first off target two things. First, show me all my Word documents, files only, on the entire C: drive, and keep the error messages to yourself, PowerShell.

Get-ChildItem -Path C:\ -Include *.doc,*.docx -File -Recurse -ErrorAction SilentlyContinue

Now I can use Where-Object to show only files that were created since the day that I stored in $FindDate. This will include everything since 12:00 AM the morning of that day. We will compare the list against the LastWriteTime property, which is the “Last Time the File was Written to.”

Get-ChildItem -Path C:\ -Include *.doc,*.docx -File -Recurse -ErrorAction SilentlyContinue | Where-Object { $_.LastWriteTime -ge $FindDate }

Hey, wait. I did some work on the weekend. I only want Friday! Well, we can filter on that, too, by using the AddDays() method to our date and give it a range of 24 hours!

Get-ChildItem -Path C:\ -Include *.doc,*.docx -File -Recurse -ErrorAction SilentlyContinue | Where-Object { $_.LastWriteTime -ge $FindDate -and $_.LastWriteTime -le $Finddate.adddays(1) }

Of course, most people might want to search only a few spots. Get-Childitem can even be told to “Search only this list of folders.” In the following example, I am going to search the C:\Users folder, an HSG folder on my USB key (Drive L: ), and a folder named “Whoops\Not\This\One” on Drive X:

Get-Childitem -Path C:\Users, L:\HSG, X:\Whoops\Not\This\One -Include HSG*.doc? -Recurse

I hope this helped you along today and empowered you a little further in your daily tasks.

I invite you to follow the Scripting Guys on Twitter and Facebook. If you have any questions, send email to them at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow.

Until then always remember that with Great PowerShell comes Great Responsibility.

Sean Kearney
Honorary Scripting Guy
Cloud and Datacenter Management MVP

Viewing all 3333 articles
Browse latest View live




Latest Images

<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>
<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596344.js" async> </script>