Showing posts with label PowerShell. Show all posts
Showing posts with label PowerShell. Show all posts

Saturday, February 18, 2017

Changing Nano back to DHCP

I've been playing around some more with Nano server, but I didn't want to create yet another VM, so I decided to use one of my existing Nano servers. But it had an IP set already, and was set to an Internal switch (so no internet connectivity possible). So let's PowerShell-fix this!

First let's change the adapter of my VM called nano1:

Get-VM nano1 |Get-VMNetworkAdapter|Connect-VMNetworkAdapter -SwitchName external

There, that didn't hurt so much! Good, now let's change the settings inside the nano1 VM, to set the IP's back to DHCP. First, this very cool thing in Hyper-V called PowerShell Direct, allows me to enter the VM without needing any IP connectivity. All you need is the credentials of the VM, and to select the "-VMName" option with Enter-PSSession:

$cred = Get-Credential
Enter-PSSession -VMName nano1 -Credential $cred

This is what it looks like:

PS C:\> $cred = Get-Credential
Enter-PSSession -VMName nano1 -Credential $cred
cmdlet Get-Credential at command pipeline position 1
Supply values for the following parameters:
[nano1]: PS C:\Users\administrator\Documents>

As Montgomery Burns says: "Excellent...": Did you notice the [nano1] in front of the last prompt? We're inside the VM! BTW, you don't need to add the credential, it will ask you for the credentials if you don't add them, but if you go in and out more than once, this is nicer.

Now that we're in a session, let's change the IP address and DNS back to DHCP enabled:

Get-NetIPAddress -InterfaceAlias "Ethernet" | Set-NetIPInterface -Dhcp Enabled
Set-DnsClientServerAddress -ResetServerAddresses -InterfaceAlias Ethernet

Done!

Sunday, January 29, 2017

F5 management through PowerShell

I've mentioned in my vCPU upgrade article that one of the requirements of me being allowed to shut down the VM was to be sure that there were no connections on the F5 loadbalancer for the webserver.

So I needed to script against the F5 loadbalancer. The first thing you need to do, is to get the F5 PowerShell Snapin from the F5 site. An account can be made for free, then you can login and download the plugin and follow the instructions to get it installed.

Once installed, you can add the snapin with:

Add-PSSnapIn iControlSnapIn

Now you have commands to manage the F5. Great, now it's time to login. First you need to add some credentials to a variable, and I've mentioned this in a previous article too. After that, you can set up the connection to the loadbalancer:

$creds = Import-CliXml -Path "D:\akos\LB01.Cred"
Initialize-F5.iControl -HostName "192.168.1.10" -Credentials $creds

Great, now you have the power to enable and disable nodes within the loadbalancer pool with actions like:

Disable-F5.LTMNodeAddress -Node "10.0.20.3"

and

Enable-F5.LTMNodeAddress -Node "10.0.20.3"

Now disabling and enabling is fine, but knowing if there's really no connections left is what you want to have. Luckily the Internet is a great place for info, and someone on Stackoverflow created the following excellent code, which I adapted a little to suit my needs. These two excellent functions do exactly what I want it to do, namely wait around for connections to drop (with a max time, don't want to spend waiting forever), and a supporting function to get the number of connections:

function WaitForConnectionsToDrop(
    [int]$MaxWaitTime = 300,
    [string]$Node
)
{
    $connections = GetCurrentConnections -Node $Node

    $elapsed = [System.Diagnostics.Stopwatch]::StartNew();
    while($connections -gt 0 -and $elapsed.ElapsedMilliseconds -lt ($MaxWaitTime * 1000)){        

        Start-Sleep -Seconds 10

        $connections = GetCurrentConnections -Node $Node
    }
}

function GetCurrentConnections(
    [string]$Node
)
{
    $ic=Get-F5.iControl
    $connections = $ic.LocalLBNodeAddress.get_statistics($Node) | foreach{$_.statistics.statistics | where {$_.type -eq "STATISTIC_SERVER_SIDE_CURRENT_CONNECTIONS"} | foreach{$_.value.low} }
    Write-Host "$Node has $connections Connections"
    return $connections
}

(note, that last few lines are broken up due to the format of the blog, it should be one line)
The great thing is, now you can easily do the following:

foreach ($node in $serverlist){
    Disable-F5.LTMNodeAddress -Node "$node"
    WaitForConnectionsToDrop -Node "$node" -MaxWaitTime 300
    #insert whatever code you want to do, like, say upgrade vCPU's, or patch the host
    Enable-F5.LTMNodeAddress -Node "$node"
}

As you can tell, it is easy to create a script with this info that will easily disable and enable nodes. Yeay, PowerShell!

Thursday, January 26, 2017

Getting rid of trailing spaces in CSV's with Powershell

There I was, banging my head against the table because I thought I was perfectly able to select stuff from a CSV file, but I could not figure out why my selections weren't showing the stuff I wanted. It turns out my CSV file entries were full of trailing spaces!

Googling around, I saw this in a forumpost somewhere, but to prevent me from losing it, I've put the script below:

$CSV = import-csv  'C:\scripts\trailingspaces.csv'
        $CSV | Foreach-Object {  
            $_.PSObject.Properties | Foreach-Object { $_.Value = $_.Value.Trim() }
        }

$CSV | ConvertTo-Csv -UseCulture -NoTypeInformation | Out-File 'C:\scripts\trimmed.csv'

Yeay, no more going mad!

Wednesday, January 25, 2017

Store credentials securely for later use

A command you use quite often is Get-Credential. For instance for creating a PSSession to a server:

$cred = Get-Credential "admin"
$s = New-PSSession -Computername myserver -Credential $cred

It's nice for on the spot use, but in a script that might not work. There are ways of doing that plaintext, but I saw a webpage that unfortunately I don't have the link for anymore, but if I find that reference I will add, but you can store those credentials securely too:

$Credential = Get-Credential
$Credential | Export-CliXml -Path "D:\akos\Myserver.Cred"

This small piece of code lets you store credentials in a directory that you can secure with NTFS rights, and also, it is encrypted, so you don't see the password in plain text in your script.

Now if you want to use those credentials again, you can just load in the file:

$cred = Import-CliXml -Path "D:\akos\Myserver.Cred"

Very nice!!

Waiting for a VM to come back

In my previous post, I wrote about upgrading vCPU's, and that I want to do several other things as well. If you upgrade a vCPU on a VM, it could very well be that the Windows guest wants a second reboot. I've found that simply rebooting and waiting for a while is inefficient. So I usually check if the VMware tools are back online.

I've made that into a function, to exactly that:

function WaitforVM($VM)
{
    while (((get-vm $VM ).ExtensionData.Guest.ToolsRunningStatus ) -ne "guestToolsRunning" )
        {
            Write-Host "Waiting for machine to come back....." -ForegroundColor Yellow
            Sleep 5
        }
}

Now all you have to do, is to start the VM after the CPU addition, wait, then restart and wait again:

Start-VM -VM $VM -Confirm:$False
WaitforVM -VM $VM
Restart-VMGuest -VM $VM -Confirm:$False
WaitforVM -VM $VM

Nice, right? Obviously this function can be used for other things, like being used in scripts when you create a new VM from template and boot it up to see if that VM is live already.

Automatically upgrade vCPU's

I am lazy. Well, not lazy, I just don't like working at ungodly hours. Here's the story: I got this request to upgrade 2 customer VM's from 3 vCPU's to 4 vcpu's. No biggy, I thought. But he wanted it done after 1 AM.. Wait, that's not during office hours!

OK, I could fix that with my trusty Powershell toolkit, and a scheduled task. But then I talked to the customer who went like: "Sure that's fine with me to script it, but make sure the sessions are drained from the webserver".. Hmm, F5 loadbalancers. I've not worked too much with those before, really. I get the general ideas within loadbalancing, but let's try and script against it.

So I had a number of things I wanted to happen:
  • Log in to the F5 loadbalancer, preferably not with a script filled with credentials hardcoded into it. 
  • Wait an X amount of time for connections to drop from the specific pool
  • Shut down the machine, and wait for it to be properly turned off.
  • Set the number of CPU's and memory. This had an extra challenge, namely that the person who built the VM put in 1 vCPU with 3 cores, instead of 3 vCPU's.
  • Send an email with the results, so I can see what happened from my phone.
I'll spend a few posts on those different things.

Firstly the vCPU upgrade, which is the basis of this entire story:

Normally, you can turn off a VM, upgrade vCPU's, then start a VM again, through this simple set of commands:

$VM=Get-VM -Name 'WEB01'

Stop-VMGuest –VM $VM –Confirm:$False
do {
        $status = (get-VM $VM).PowerState
    }until($status -eq "PoweredOff")

$VM | Set-VM -NumpCPU 4 –Confirm:$False | Start-VM

However, since someone used 1 vCPU with multiple cores instead of multiple vCPU sockets, things work a little differently. If you would try this command, the VM would be shut down, but no upgrade would happen, and start back up with the same amount as before.

Fortunately there's another trick for that:

$VMSpec = New-Object -Type VMware.Vim.VirtualMachineConfigSpec -Property @{"NumCoresPerSocket" = 4;"numCPUs" = 4}
$VM.ExtensionData.ReconfigVM_Task($VMSpec)

(That first line shouldn't be cut off like that, should be 1 line, but alas: blog template gets in the way)

Now the VM gets upgraded to 4 vCPU's, although you would think it'd be 4x4 cpu's looking at the syntax.

So now the final complete code would be:

Add-PSsnapin VMware.VimAutomation.Core
Connect-VIServer myVcenter -ErrorAction Stop


$VM=Get-VM -Name 'WEB01'


Stop-VMGuest –VM $VM –Confirm:$False
do {
        $status = (get-VM $VM).PowerState
    }until($status -eq "PoweredOff")


$VMSpec = New-Object -Type VMware.Vim.VirtualMachineConfigSpec -Property @{"NumCoresPerSocket" = 4;"numCPUs" = 4}


Start-VM $VM


Yeay, success! Now on to the next bit...

Friday, July 29, 2016

Selecting and removing old snapshots

Me: "Say Customer X, I see you still have some snapshots here and there. Can I remove them? It's not wise to keep them for a long time."
Customer X: "Oh yeah, I forgot about those. Well: those updates we did worked out fine, so anything from before 11-07-2016 can be deleted"

Get-VM | Get-Snapshot | Where {$_.Created -lt (Get-Date 11-07-2016) | Remove-Snapshot -confirm:$false

Me: "OK, done!"

Another possibility: "Can you give me a list of snapshots that are older than a week?"

Get-VM | Get-Snapshot | Where {$_.Created -lt ((Get-Date).AddDays(-7))} | Select VM, Name

Did I say how much I love Powershell/PowerCLI? ;-)

Wednesday, July 27, 2016

Get the serialnumber via Powershell

I quickly wanted to know a serial number for a certain HP Server, but didn't want to log on to the ILO card to get it from there. You don't have to, either:


gwmi win32_bios | fl SerialNumber

Works on most machines I've seen so far (HP and Dell).

Thursday, July 21, 2016

Decode AsSecureString into cleartext

I needed a way to enter a password from the console. This is done by typing:

$pwd = Read-Host

It works, but I didn't want this text to be visible on the screen while I'm typing it:

$pwd = Read-Host -AsSecureString

Success! Now let's see the password in action:

PS C:\Users\Akos> $pwd
System.Security.SecureString

.. Hmm, not quite what I had hoped for. But there is hope yet:

$cleartextpwd = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($pwd))

Decodes the secure string into cleartext! Now you can type a password securely and use it. Be sure to remove the variable once you're done ;-)

Saturday, June 25, 2016

Using Cloudstack with Powershell

If you are trying to work with Powershell with a Cloudstack environment through the REST API, you will soon find that there aren't that many solutions available. For Linux/Python environments there is Cloudmonkey which is excellent, but there is no native "VMware PowerCLI" like tool available from either Apache or Citrix, and even googling for Cloudstack and Powershell give basically just two Github projects:

  • A project that was last edited 3 years ago on Github from fifthecho
  • A project on Github from one of the engineers of Schuberg Philis, called psCloudstack

There is a project from Exoscale as well, but that is basically the Github contents from fifthecho, with perhaps some additions to it, and also from 2013.

Fifthecho
The fifthecho project consists of a number of Powershell functions spread over a few.ps1 files, and a Cloudstack module that is called by every ps1 file to give the core functionality to talk to the Cloudstack API. Unfortunately it is not actively maintained, and all the files have an expired signature giving you an error when you try to execute the Powershell scripts. You can get around this by doing:

Set-ExecutionPolicy -ExecutionPolicy Bypass -Scope Process

Basically for the Powershell session you are in, you are allowing everything, so it is not very elegant. Next to this, the Powershell scripts that are delivered only cover a small fraction of what is possible through the API (10 or so out of almost 250), and the way it is programmed is a bit dated, and I didn't feel like spending ages to create an extensive library myself, so I will let that be.

psCloudstack
Then there is psCloudstack. I saw a video about this module a while ago from the creator himself, Hans van Veen, and I was impressed by the capability. This Powershell module takes a different approach: Instead of having a bunch of Powershell commands that talk to the API, it lists all the API commands (by virtue of the "listApis" command in the Cloudstack API library), and builds actual Powershell functions from them, complete with get-help functionality. These functions reside in memory, and will need to be "re-built" whenever you close the Powershell window. The elegance of this is that whenever you have a new version of Cloudstack, you have the complete API set to your disposal.

I had issues with getting the module to work though, but since it was the most comprehensive Cloudstack module for Powershell, I wanted to make it work, so I took some time to troubleshoot what happens. I'll go over installation first:

Installation
To download the module, go to the psCloudstack Github page, and download the files.

Create a module directory if you don't have one:

$PSModulePath = $Env:PSModulePath -split ";" | Select -Index ([int][bool]$Global)
New-Item -ItemType Directory $PSModulePath\psCloudstack

Then unzip all psCloudstack files into that directory (especially the .psm1 and psd1 file). Windows recognizes things that are downloaded from the Internet so you need to rightclick the psm1 and psd1 file and select Properties, then click the Unblock button.


If you have a Windows 10 machine, or have WMF5 installed, you can do it with this script:

$PSModulePath = $Env:PSModulePath -split ";" | Select -Index ([int][bool]$Global)
New-Item -ItemType Directory $PSModulePath\psCloudstack
Invoke-WebRequest -Uri "https://raw.githubusercontent.com/schubergphilis/psCloudstack/master/PSCloudstack.psm1" -OutFile $PSModulePath\psCloudstack\PSCloudstack.psm1
Invoke-WebRequest -Uri "https://raw.githubusercontent.com/schubergphilis/psCloudstack/master/PSCloudstack.psd1" -OutFile $PSModulePath\psCloudstack\PSCloudstack.psd1
Unblock-File $PSModulePath\psCloudstack\PSCloudstack.psd1
Unblock-File $PSModulePath\psCloudstack\PSCloudstack.psm1

Configuration
So, now you have psCloudstack on your machine. Time to add a configuration. If you list the commands from the module you get the following list:

PS C:\Users\Akos\Documents\WindowsPowerShell\Modules> Get-Command -Module pscloudstack

CommandType     Name                                               Version    Source                            
-----------     ----                                               -------    ------                            
Function        Add-CSConfig                                       3.2.1      pscloudstack                      
Function        Connect-CSManager                                  3.2.1      pscloudstack                      
Function        Convert-CSConfig                                   3.2.1      pscloudstack                      
Function        Get-CSConfig                                       3.2.1      pscloudstack                      
Function        Invoke-CSApiCall                                   3.2.1      pscloudstack                      
Function        Remove-CSConfig                                    3.2.1      pscloudstack                      
Function        Set-CSConfig                                       3.2.1      pscloudstack                      
Function        Start-CSConsoleSession                             3.2.1      pscloudstack                      

 
Most commands in the module have to do with the configuration, but we just need Add-CSConfig, so let's look how we should use it:
PS C:\Users\Akos\Documents\WindowsPowerShell\Modules> get-help Add-CSConfig

NAME
    Add-CSConfig
    
SYNOPSIS
    Adds connection configuration information to the configuration file
    
    
SYNTAX
    Add-CSConfig [[-Zone] ] [-Server]  [[-SecurePort] ] [[-UnsecurePort] ] 
    [-Apikey]  [-Secret]  [-UseSSL] [[-ConfigFile] ] []
    
    
DESCRIPTION
    This function is used to add connection configuration information to the configuration file.



PS C:\Users\Akos\Documents\WindowsPowerShell\Modules> get-help Add-CSConfig -Examples

NAME
    Add-CSConfig
    
SYNOPSIS
    Adds connection configuration information to the configuration file
    
    -------------------------- EXAMPLE 1 --------------------------
    
    PS C:\># Create/Update the content of the default config file
    
    
    C:\PS> Add-CSConfig -Server www.xxx.yyy.zzz -Api xxxxxxx -Secret yyyyyyyyy

 
This, unfortunately, is where the first "bug" occurs for me, when trying to execute this command. When executing this command without the -UseSSL switch you get an error:

Add-CSConfig -Server https://myservices.interoute.com/myservices/api/vdc -Apikey xxx -Secret yyy
Add-CSConfig : Cannot process argument transformation on parameter 'UseSSL'. Cannot convert value "System.String" to type "System.Management.Automation.SwitchParameter". Boolean parameters accept only Boolean values and numbers, such as $True, $False, 1 or 0.

When try use it with the -UseSSL (and without the "https://" prefix, there is no feedback, so let's try to connect:
Add-CSConfig -Server myservices.interoute.com/myservices/api/vdc -Apikey xxx -Secret yyy -UseSSL

PS C:\Users\Akos\Documents\WindowsPowerShell\Modules> Connect-CSManager
Welcome to psCloudstack V3.2.1, ...
API Call Error: 
    
    Oops...
    
                    The application has encountered an unknown error.
                    Our technical staff have been automatically notified and will be looking into this with the u
tmost urgency.
                
            
  psCloudstack-version displaytext                                                                                
-------------------- -----------                                                                                
3.2.1                ...                                                                                        

Whoops, what happened?.. It was time to start looking at the module itself, because something was happening, but I didn't know what. I see that by default, the config file is put in $env:LocalAppData (a.k.a. C:\Users\<username>\AppData\Local ), so when I look at that file, I see unusual ports being used (8080 and 8096):



Instead of trying to redo the Add-CSConfig, I decided to edit the file directly, putting 443 an 80. Now lets try again:

PS C:\Users\Akos\Documents\WindowsPowerShell\Modules> Connect-CSManager
Welcome to psCloudstack V3.2.1, ...
API Call Error: 
    
    Oops...
    
                    The application has encountered an unknown error.
                    Our technical staff have been automatically notified and will be looking into this with the u
tmost urgency.
                
            
  psCloudstack-version displaytext                                                                                
-------------------- -----------                                                                                
3.2.1                ...                                                                                        

More troubleshooting was needed. I will spare you the gritty details, but eventually I used the fifthecho module to create a connectionstring that I knew worked, and compared it to the connectionstring that the psCloudstack module produced, and I found what was the issue for me:

In line 1036, there is the following statement:

$baseUrl = "{0}://{1}:{2}/client/{3}?{4}" -f $Protocol, $InputObject.Server, $Port, $InputObject.Type, $InputObject.Command

which creates something like https://url:443/client/api?convertedstringwhichholdsapicall.

In the mean time, the Cloudstack implementation from Interoute VDC wants:

https://url/?convertedstringwhichholdsapicall

So by changing the way the string is connecting, the problem should be solved. Comment out the line, and put the line below under it.

#$baseUrl = "{0}://{1}:{2}/client/{3}?{4}" -f $Protocol, $InputObject.Server, $Port, $InputObject.Type, $InputObject.Command
$baseUrl = "{0}://{1}?{2}" -f $Protocol, $InputObject.Server, $InputObject.Command

Now let's try it once more  (do a "remove-module psCloudstack" first to reload the module):

PS C:\Users\Akos\Documents\WindowsPowerShell\Modules> Connect-CSManager
Welcome to psCloudstack V3.2.1, ...generating 252 api functions for you

Yeay, success! Now when you do Get-Command -Module psCloudstack you'll see a whole bunch of functions. These functions are the actual API calls that you can make to the Cloudstack environment:

PS C:\Users\Akos\Documents\WindowsPowerShell\Modules> Get-Command -Module psCloudstack

CommandType     Name                                               Version    Source                            
-----------     ----                                               -------    ------                            
Function        activateProject                                    3.2.1      PSCloudstack                      
Function        addAccountToProject                                3.2.1      PSCloudstack                      
Function        Add-CSConfig                                       3.2.1      PSCloudstack                      
Function        addIpToNic                                         3.2.1      PSCloudstack                      
Function        addNicToVirtualMachine                             3.2.1      PSCloudstack                      
Function        addVpnUser                                         3.2.1      PSCloudstack                      
Function        archiveEvents                                      3.2.1      PSCloudstack                      
Function        assignCertToLoadBalancer                           3.2.1      PSCloudstack                      
Function        assignToGlobalLoadBalancerRule                     3.2.1      PSCloudstack                      
Function        assignToLoadBalancerRule                           3.2.1      PSCloudstack                      
Function        assignVirtualMachine                               3.2.1      PSCloudstack                      
Function        associateIpAddress                                 3.2.1      PSCloudstack                      
Function        attachIso                                          3.2.1      PSCloudstack                      
Function        attachVolume                                       3.2.1      PSCloudstack                      
Function        authorizeSecurityGroupEgress                       3.2.1      PSCloudstack                      
Function        authorizeSecurityGroupIngress                      3.2.1      PSCloudstack                      
Function        changeServiceForRouter                             3.2.1      PSCloudstack                      
Function        changeServiceForVirtualMachine                     3.2.1      PSCloudstack                      
Function        configureInternalLoadBalancerElement               3.2.1      PSCloudstack                      
Function        configureVirtualRouterElement                      3.2.1      PSCloudstack                      
Function        Connect-CSManager                                  3.2.1      PSCloudstack                      
Function        Convert-CSConfig                                   3.2.1      PSCloudstack      
.
.
.

252 modules are created. Note that your Cloudstack provider could deny access to some API calls.

Example to list and create VM's:
PS C:\Users\Akos\Documents\WindowsPowerShell\Modules> listVirtualMachines |select name, templatename

name     templatename      
----     ------------      
server01 ubuntu1404_minimal

Awesome! and when you do Get-Help on one of these functions, you see actual help: Super stuff from Hans van Veen. He is maintaining this module, and even while I was writing this blogpost, he updated the module again. Unfortunately, the most recent update he made will give you an error, as it is trying to use some API calls that have been disallowed from usage by the Interoute VDC API (which is using Cloudstack), but the functions are still created and work.

I may want to modify the module so that the functions don't just live in memory (and you first need to run Connect-CSManager), but are in a module of its own, and I may want to modify the connection functionality, so it "just works" (at least with the Cloudstack environment that I have at my disposal).

Somewhere in another post I will also add some examples to use this excellent module.

Thursday, June 16, 2016

Desired State Configuration - Part 1

I have been diving into Powershell Desired State Configuration lately, as the technology is now a few years old and a little more mature, especially with the coming of Powershell 5. Many in the industry named this technology the technology that you should learn last year or the year before that already and not to mention that Microsoft keeps hammering on it that you should learn about it.

So what is Powershell DSC? In its core it is a configuration management platform. It allows for systems and applications to be installed and configured by means of a configuration document, and can manage configuration drift. Configuration management tools are not new, and have been around for a while with other tools like Puppet, Chef, Ansible, Salt and others.They serve the same purpose with varying features, but are mostly catered to Linux platforms. The same way that Powershell was an answer to having a proper commandline automation tool for Windows that supported its .Net and object oriented nature, DSC is Microsoft's answer to Configuration Management. But Microsoft went further by making DSC a platform that other tools use. So instead of competing against companies like Pupped and Chef, Microsoft has actively engaged with these companies so they could use the technology to better automatically configure Windows systems and applications.


Powershell and DSC inventor Jeffrey Snover describes DSC as captain Picard on the Starship Enterprise ordering Riker to go to that distant planet by saying "Make it so number one", and Riker executes that order: Picard desires something, and Riker "makes it so". DSC is basically the same thing inside your computer: With DSC, you desire something to be in a certain state through a configuration file and the local agent (called the Local Configuration Manager a.k.a. LCM) makes it so. This LCM is not all knowing in itself, but it uses things called DSC Resources to do the tasks you want it to do. These resources are now rapidly being developed by both Microsoft as well as the community around it, but if you can't find the thing you want to have configured, you can create them yourself. The main source for resources is the Powershell Gallery, and Powershell 5 has got the PowerShellGet module which now lets you install modules straight from this repository, just like you would use apt-get from a linux repository.

What makes DSC so cool?
  • You can deploy a system complete with applications and settings, nuke it and deploy the exact same machine (which makes devs happy because your dev matches your prod environment exactly)
  • Your OS/application is as you documented it in your configuration file, and configuration drift can be corrected by DSC
  • The DSC configuration is a Powershell based script, so you can use Powershell for variables and such, and do troubleshooting with it too
  • DSC can be used not only for Windows systems, but is slowly making its way to hardware devices too (e.g. Cisco Nexus)
  • There's linux support too, but as far as I've seen it, that's a bit of a work in progress. Cool nonetheless. 
  • Puppet/Chef/Salt/Ansible support the use of the DSC resources!
  • Windows Nano Server doesn't support Group Policies, but it supports DSC instead
There are more cool things about it, and DSC resources are added daily to make it even cooler. In another post I will go over the more technical details.

Thursday, April 14, 2016

Surpress SSH and shell warnings on ESXi

Small oneliner to surpress the warning you get when you enable SSH:



Like so:

Get-VMHost | Set-VmHostAdvancedConfiguration -Name UserVars.SuppressShellWarning -Value 1

Monday, March 14, 2016

Wait-Job, how nice

A few posts ago I had a bit of a tacky way to wait until a job is done:

While (Get-Job -State "Running") {
    Write-Host -ForegroundColor Yellow "Running..."
    Start-Sleep 1
}

Turns out, there is a much more elegant function to do this:

Wait-Job -State Running

or as the built in example shows:

Get-Job | Wait-Job

This command waits for all of the background jobs running in the session to complete. Nice!

Friday, March 11, 2016

Beautify HTML output from ConvertTo-HTML

Quick one to write down before I forget it again, and have to search the interwebs again:

Whenever you use ConvertTo-Html, the output is usually.. meh. However, you can add some CSS styling and include it in the converting process:

$Header = @"

<style>

Body{background-color:white;font-family:Arial;font-size:10pt;}

Table{border-width: 1px; border-style: solid; border-color: black; border-collapse: collapse;}

TH{border-width: 1px; padding: 2px; border-style: solid; border-color: black; background-color: #cccccc;}

TD{border-width: 1px; padding: 5px; border-style: solid; border-color: black; background-color: white;}

</style>

"@

Get-Process | ConvertTo-Html -Head $Header | Out-File C:\temp\process.htm


Which gives a little more oomph to your HTML output:



You can also put the CSS code in a separate file (e,g, D:\table.css), and include the CSS with
ConvertTo-Html -CssUri "D:\table.css"

Thursday, February 25, 2016

Getting an overview of reboots revisited

In my previous post I mentioned invoke-command using the -AsJob option. But then I saw a post from Tommy Maynard that made things even quicker and certainly a bit easier.

Instead of doing a "foreach server in serverlist" loop to run the invoke-command scriptblock, you can do an invoke-command on the entire serverlist in one go!

$ServerList = Get-Content C:\Scripts\ServerList.txt.txt

$block = {Get-EventLog System | Where-Object {$_.EventID -eq "1074" -or $_.EventID -eq "6008" -or $_.EventID -eq "1076"} | Select Machinename, TimeWritten, UserName, EventID, Message}

Invoke-Command -ComputerName $ServerList -ScriptBlock $block | ft -AutoSize -Wrap |Out-File C:\scripts\ServerRestart.txt

This certainly cuts down on scriptsize :-)

How much time you save depends on the amount of servers, but I tested it with 5 VM's, and saw some surprising things in my test: Invoke-command feeding the serverlist as described above actually took longer than invoke-command using the -Asjob option.

Usually I get 14 seconds for the old fashioned "foreach" test, then about 6 seconds for the invoke-command feeding the serverlist, and about 2 seconds for the invoke-command with -Asjob.

Here's the test I ran a few times after another:

$ServerList = Get-Content C:\Scripts\ServerList.txt.txt

# Test 1: Old fashioned way

Write-Host -ForegroundColor green "Foreach server in serverlist"
Measure-Command {foreach ($server in $ServerList){Get-EventLog System -ComputerName "$server" | Where-Object {$_.EventID -eq "1074" -or $_.EventID -eq "6008" -or $_.EventID -eq "1076"} | ft Machinename, TimeWritten, UserName, EventID, Message -AutoSize}}|select Seconds|ft -AutoSize

# Test 2: Doing the test with invoke-command, feeding the serverlist in one go

$block = {Get-EventLog System | Where-Object {$_.EventID -eq "1074" -or $_.EventID -eq "6008" -or $_.EventID -eq "1076"}}

Write-Host -ForegroundColor green "invoke with serverlist"
Measure-Command {Invoke-Command -ComputerName $ServerList -ScriptBlock $block}|select Seconds|ft -AutoSize

# Test 3: Doing the test with "Job" functionality

Write-Host -ForegroundColor green "Job Functionality"
Measure-Command {
foreach ($Server in $ServerList){
Invoke-Command -ComputerName $Server -ScriptBlock $block -AsJob
}

While (Get-Job -State "Running") {
}    

Get-Job| Receive-Job |ft -AutoSize
Get-Job | Remove-Job
Write-Host -ForegroundColor Yellow "Done!"
}|select Seconds|ft -AutoSize

The weird thing however, is if I swap test 2 and 3 around, invoke-command using -Asjob consistently works slower than the invoke-command with serverlist.  The old fashioned test still took  14 seconds, but the invoke-command with -Asjob took 2 to 6 seconds, and invoke-command feeding the serverlist consistently took 1 second.

Last try:

So I wasn't sure why the last piece of the script went faster than the middle part, but swapping it consistently made the latter one faster. So I ran each of the two tests in a loop for about 10 times in a row, and lo and behold: The invoke-command using -Asjob was running at 1-2 seconds, and feeding the serverlist in one go ran consistently for 1 second. For both tests, the first run took about 6 seconds, and this could be the same delay that is apparent in the test I listed above.

I assume that somehow a connection is made using Invoke-Command, and this session somehow stays alive.

In the end: Feed invoke-command the serverlist as a whole. It's easier to manage. and a little bit faster consistently.

Tuesday, February 16, 2016

SSH via Powershell

In hosting environments, you rarely see only one flavor of operating system, so to be able to manage linux systems with Powershell via SSH is a big help. With Windows Server 2016, SSH connectivity will be built in, but until that time you can quite easily get it right now.

Step 1:
Go to https://sshnet.codeplex.com and download the .Net 4.0 binary from the downloads section and place the "Renci.SshNet.dll" file somewhere where you can access it. In my example I put it in C:\Scripts

Most likely Chrome will ask you whether it is safe to download, obviously say "Keep":







Step 2:
Go to the file, rightclick and select Properties, then unblock the file.



Step 3:
Here comes the example code. Quick explanation: First you load the DLL, then you create an object with the DLL file (I'm sure there's a technical programmy way of saying this, but I have no clue), then you suddenly have all these cool features like "Connect" and "RunCommand". It is not an interactive thing (as far as I know), and you only have a single command you can run, so you can't create a scriptblock and run it all at once.

#Load the SSHNet Library
[void][reflection.assembly]::LoadFrom( (Resolve-Path "C:\Scripts\Renci.SshNet.dll") )

#The variables you need to log on
$server = "Server01"
$login = "someuser"
$password = "somepassword"


#Call the SSHNet Library as an object
$SshClient = New-Object Renci.SshNet.SshClient($server, 22, $login, $password)
#Connect to client using SSH
  $SshClient.Connect()
    
   if ($SshClient.IsConnected) {
    Write-Host "Connected to " $server
    #Run the command on the linux host
    $cmd = $SshClient.RunCommand('ls -l')
    
    if($cmd.Result -ne '')
    {
        Write-Host Output from $server
        Write-Host $cmd.Result
    }
    
  }
$SshClient.Disconnect()
$SshClient.Dispose()

Which gives the following output:

Connected to  Server01
Output from Server01
total 12
-rw-r--r-- 1 Akos users 136 Feb 21  2008 local.cshrc
-rw-r--r-- 1 Akos users 157 Feb 21  2008 local.login
-rw-r--r-- 1 Akos users 174 Feb 21  2008 local.profile

Of course this is a simple example, but you can load in a CSV file with server, login and password info, and then have a command run on however many servers as you want. It's always fun being able to tell your boss that you're doing that thing on 50 servers, and then getting a cup of tea while your script is doing your work for you. This SSHNet client can also upload files using sftp but I haven't tried that yet.