0

Rise of Docker – the game changer technology

If you have not yet been working with Docker containers – worse… if you have not yet really hard about Docker and significant changes it brings – then you should find more information!

In simple words Docker does the thing we always wanted – it isolates applications from our hosts layer. This enables possibility of creating micro services that can be dynamically scaled / updated / re-deployed !

If you would like to imagine how this whole Docker works then I’m sure that by looking on image below you will get the grasp of the idea behind!

DockerWithWindowsSrvAndLinux

 

 

So things to keep in mind are that Docker is not some black magic box … it requires underlying host components to run on top of it. What I mean by that If you need to run Windows containers you will need Windows host and the same principal will apply for the Linux container. You will also need Linux Host.

Up to this point there was no real support on Docker containers on Windows. However by the time of writing this document Microsoft has released Windows Server 2016 which brings major breaking changes and primarly of our interest the support to containers!

One of the thing that Microsoft have made people aware is that you will be able to  manage containers with Docker and with Powershell … but …. yep there is a but – containers created with one cannot be managed with other one . I think thats a fair trade of but thats something that potentially is going to be changed.

 

In the meantime I invite you to explore docker hub and help yourself by getting more detailed information when exploring the docker docs 

In one of the next posts we will discuss how to get Windows docker container running on Windows Server 2016 (TP3 ) !  With that keep intro to Docker I hope to see you again!

 

 

0

Manage your GitHub gists with GistboxApp

If you are like me 😀 and understand that sharing with your knowledge is something great – then this application might be for you. I dont have yet so many Gists on GitHub but getting them correctly managed is something I wanted to have.

Thats how I came across GistboxApp which I personally think its a great value added for anyone having to manage gists. It gives you option of creating labels , adding files , editing … and much more. I think if you use GitHub you might an to give it a spin.

2015-08-28_15h13_57

 

 

It also has Chrome cool addon which enables you to quickly create gits on the fly!

 

Looking out to hear from you what else are you using 😀

0

PowerShell – using PSDefaultParameterValues to make your life easier

Just a quick post to let you know that you can make your life easier (most of the times 😀 ) when for example using default paramaters for your PowerShell cmdlets. This is done by using $PSDefaultParameterValues which allows you to specify default parameters for parameters in your cmdlets. It will be available in session in which you are working in.

Example ?

 

$PSDefaultParameterValues.Add('Invoke-WebRequest:Proxy','http://comeproxy.server:80') 
$PSDefaultParameterValues.Add('Invoke-WebRequest:ProxyUseDefaultCredentials',$true)

 

Just remember that those settings will alive only in your current powershell session.

 

 

2

Powershell – Connect to TFS wiith alternative credentials

In this short post I will just show you how can you connect to your TFS server (from PowerShell) using alternative credentials. As it might be useful when you automate your infra to peform certain task. This script assumes you do have required TFS dlls available on your machine. If not you need to get them and load them on runtime

This is how I load it in a seperate parts of my scripts

        # load the required dll
        [void][System.Reflection.Assembly]::LoadWithPartialName('Microsoft.TeamFoundation.Client')

 

Here is the script itself …

0

Splunk – Network components diagram

If you have been using Splunk (or need to make a design for that system ) it is handy to visualize how Splunk works and what ports you are looking for.

While browsing Splunk forum base I have came across this great diagram ( link to source *thx for finding that Matthijs  )

spluk

 

 

I think this is really helpfull to see exactly how does components work with each other.

0

PowerShell – Creating DSC module – Part 3

logo-powershell-520x245We continue today with our DSC module creation. If you have been following this blog in recent days you might have noticed me posting information about good practices and idea I had to create this module. To keep up with the series you might want to visit :

  1. Part 1
  2. Part 2
  3. DSC Best practices

Since we can say we get smarter everyday its time to do …

Recap :

  • First of all we need to re-think the whole purpose of creating 2 resources for appPool management (here I refer to managing appPool defaults and remaining appPools). Why should we reconsider ? Well simply because with my original approach I’m unnecessarly duplicating data. As every application pool has the same properties as the default one … whats the point of having 2 resources ? well there isnt – so we are going to change that
  • Another one is appropiate naming of script responsible for naming resource. So lets go and start off with…

Structuring files the right way

By following point 21 from this recently mentioned DSC checklist we will be putting script which creates our resources into folder

C:\Program Files\WindowsPowerShell\Modules\cWebAdmin\DSCResources\RafPe_cWebAppPool\ResourceDesignerScripts

The file will be named accordingly to best practices – so using Generate<ResourceName>Schema.ps1 . So I have ended up having GenerateCwebAppPoolSchema.ps1

So we got this sorted. I have cleaned up GitHub project from unnecessary files and now it will only contain resource for cWebAppPool

 

Creating DSC resource

So now after all of this excercises we can go ahead and create our resource. At this stage 🙂 there is no guaratee that it will all work out of the box 😀 but we will do our best.  You think probably that you can get the coffee before we finish…. well not really 😀 I have prepared the file already for you so by just copying the code below we will be able to get our resource in no time (up to date version of this file in as usual on GiHub – code showed here is for learning purposes more )

   $Ensure                         = New-xDscResourceProperty -Name Ensure -Type String -Attribute Write -ValidateSet 'Present', 'Absent'
   $name                           = New-xDscResourceProperty –Name Name –Type String –Attribute Key		
   $queueLength                    = New-xDscResourceProperty –Name queueLength –Type String –Attribute Write		
   $autoStart                      = New-xDscResourceProperty –Name autoStart –Type String –Attribute Write
   $enable32BitAppOnWin64          = New-xDscResourceProperty –Name enable32BitAppOnWin64 –Type string –Attribute Write -ValidateSet 'true','false'
   $managedRuntimeVersion          = New-xDscResourceProperty –Name managedRuntimeVersion –Type string –Attribute Write -ValidateSet 'v4.0','v2.0',''
   $managedRuntimeLoader           = New-xDscResourceProperty –Name managedRuntimeLoader –Type string –Attribute Write
   $enableConfigurationOverride    = New-xDscResourceProperty –Name enableConfigurationOverride –Type string –Attribute Write  -ValidateSet 'true','false'
   $managedPipelineMode            = New-xDscResourceProperty –Name managedPipelineMode –Type string –Attribute Write -ValidateSet 'Integrated','Classic'
   $CLRConfigFile                  = New-xDscResourceProperty –Name CLRConfigFile –Type string –Attribute Write
   $passAnonymousToken             = New-xDscResourceProperty –Name passAnonymousToken –Type string –Attribute Write -ValidateSet 'true','false'
   $startMode                      = New-xDscResourceProperty –Name startMode –Type string –Attribute Write -ValidateSet 'AlwaysRunning','OnDemand'
   $identityType                   = New-xDscResourceProperty –Name identityType –Type string –Attribute Write -ValidateSet 'ApplicationPoolIdentity','LocalSystem','LocalService','NetworkService','SpecificUser'
   $userName                       = New-xDscResourceProperty –Name userName –Type string –Attribute Write
   $password                       = New-xDscResourceProperty –Name Password –Type PSCredential –Attribute Write
   $loadUserProfile                = New-xDscResourceProperty –Name loadUserProfile –Type string –Attribute Write -ValidateSet 'true','false'
   $setProfileEnvironment          = New-xDscResourceProperty –Name setProfileEnvironment –Type string –Attribute Write -ValidateSet 'true','false'
   $logonType                      = New-xDscResourceProperty –Name logonType –Type string –Attribute Write -ValidateSet 'LogonBatch','LogonService'
   $manualGroupMembership          = New-xDscResourceProperty –Name manualGroupMembership –Type string –Attribute Write  -ValidateSet 'true','false'
   $idleTimeout                    = New-xDscResourceProperty –Name idleTimeout –Type string –Attribute Write
   $idleTimeoutAction              = New-xDscResourceProperty –Name idleTimeoutAction –Type string –Attribute Write -ValidateSet 'Terminate','Suspend'
   $maxProcesses                   = New-xDscResourceProperty –Name maxProcesses –Type string –Attribute Write
   $shutdownTimeLimit              = New-xDscResourceProperty –Name shutdownTimeLimit –Type string –Attribute Write
   $startupTimeLimit               = New-xDscResourceProperty –Name startupTimeLimit –Type string –Attribute Write
   $pingingEnabled                 = New-xDscResourceProperty –Name pingingEnabled –Type string –Attribute Write -ValidateSet 'true','false'
   $pingInterval                   = New-xDscResourceProperty –Name pingInterval –Type string –Attribute Write
   $pingResponseTime               = New-xDscResourceProperty –Name pingResponseTime –Type string –Attribute Write
   $disallowOverlappingRotation    = New-xDscResourceProperty –Name disallowOverlappingRotation –Type string –Attribute Write -ValidateSet 'true','false'
   $disallowRotationOnConfigChange = New-xDscResourceProperty –Name disallowRotationOnConfigChange –Type string –Attribute Write -ValidateSet 'true','false'
   $logEventOnRecycle              = New-xDscResourceProperty –Name logEventOnRecycle –Type string –Attribute Write
   $memory                         = New-xDscResourceProperty –Name memory –Type string –Attribute Write
   $privateMemory                  = New-xDscResourceProperty –Name privateMemory –Type string –Attribute Write
   $requests                       = New-xDscResourceProperty –Name requests –Type string –Attribute Write
   $time                           = New-xDscResourceProperty –Name time –Type String –Attribute Write
   $schedule                       = New-xDscResourceProperty –Name schedule –Type Hashtable[] –Attribute Write
   $loadBalancerCapabilities       = New-xDscResourceProperty –Name loadBalancerCapabilities –Type string –Attribute Write -ValidateSet 'HttpLevel','TcpLevel'
   $orphanWorkerProcess            = New-xDscResourceProperty –Name orphanWorkerProcess –Type string –Attribute Write -ValidateSet 'true','false'
   $orphanActionExe                = New-xDscResourceProperty –Name orphanActionExe –Type string –Attribute Write
   $orphanActionParams             = New-xDscResourceProperty –Name orphanActionParams –Type string –Attribute Write
   $rapidFailProtection            = New-xDscResourceProperty –Name rapidFailProtection –Type string –Attribute Write -ValidateSet 'true','false'
   $rapidFailProtectionInterval    = New-xDscResourceProperty –Name rapidFailProtectionInterval –Type string –Attribute Write
   $rapidFailProtectionMaxCrashes  = New-xDscResourceProperty –Name rapidFailProtectionMaxCrashes –Type string –Attribute Write
   $autoShutdownExe                = New-xDscResourceProperty –Name autoShutdownExe –Type string –Attribute Write
   $autoShutdownParams             = New-xDscResourceProperty –Name autoShutdownParams –Type string –Attribute Write
   $limit                          = New-xDscResourceProperty –Name limit –Type string –Attribute Write
   $action                         = New-xDscResourceProperty –Name action –Type string –Attribute Write -ValidateSet 'NoAcion','KillW3wp','Throttle','ThrottleUnderLoad'
   $resetInterval                  = New-xDscResourceProperty –Name resetInterval –Type string –Attribute Write
   $smpAffinitized                 = New-xDscResourceProperty –Name smpAffinitized –Type string –Attribute Write -ValidateSet 'true','false'
   $smpProcessorAffinityMask       = New-xDscResourceProperty –Name smpProcessorAffinityMask –Type string –Attribute Write
   $smpProcessorAffinityMask2      = New-xDscResourceProperty –Name smpProcessorAffinityMask2 –Type string –Attribute Write
   $processorGroup                 = New-xDscResourceProperty –Name processorGroup –Type string –Attribute Write
   $numaNodeAssignment             = New-xDscResourceProperty –Name numaNodeAssignment –Type string –Attribute Write -ValidateSet 'MostAvailableMemory','WindowsScheduling'
   $numaNodeAffinityMode           = New-xDscResourceProperty –Name numaNodeAffinityMode –Type string –Attribute Write -ValidateSet 'Soft','Hard'

#array to hold our properties
$xDscProperties [email protected](
       $Ensure
       $name,
       $queueLength,
       $autoStart,
       $enable32BitAppOnWin64,
       $managedRuntimeVersion,
       $managedRuntimeLoader,
       $enableConfigurationOverride,
       $managedPipelineMode,
       $CLRConfigFile,
       $passAnonymousToken,
       $startMode,
       $identityType,
       $userName,
       $password,
       $loadUserProfile,
       $setProfileEnvironment,
       $logonType,
       $manualGroupMembership,
       $idleTimeout,
       $idleTimeoutAction,
       $maxProcesses,
       $shutdownTimeLimit,
       $startupTimeLimit,
       $pingingEnabled,
       $pingInterval,
       $pingResponseTime,
       $disallowOverlappingRotation,
       $disallowRotationOnConfigChange,
       $logEventOnRecycle,
       $memory,
       $privateMemory,
       $requests,
       $time,
       $schedule,
       $loadBalancerCapabilities,
       $orphanWorkerProcess,
       $orphanActionExe,
       $orphanActionParams,
       $rapidFailProtection,
       $rapidFailProtectionInterval,
       $rapidFailProtectionMaxCrashes,
       $autoShutdownExe,
       $autoShutdownParams,
       $limit,
       $action,
       $resetInterval,
       $smpAffinitized,
       $smpProcessorAffinityMask,
       $smpProcessorAffinityMask2,
       $processorGroup,
       $numaNodeAssignment,
       $numaNodeAffinityMode      
    )

# Create resource that will be creating our application pool 
New-xDscResource -Name RafPe_cWebAppPool`
                 -FriendlyName cWebAppPool`
                 -ModuleName cWebAdmin`
                 -Path 'C:\Program Files\WindowsPowerShell\Modules' `
                 -Property $xDscProperties -Verbose

 

So what happened here ? We have created a lot of DSC resource properties and then used array to hold those values. Why array ? Well because then its simple to pass it into the command which has created the resource for us. New-xDscResource 

You may noticed that I have used RafPe_cWebAppPool as the name. This is because I’m not the only one creating names and they for obvious reasons cannot be the same. Therefore further as parameter we have friendly name which I set to be cWebAppPool. Next is quite important as it is our module name which I have decided to be cWebAdmin as purpose of this module is to manage not only app Pool but much more on IIS webservers . Lastly its path (which I use the default ones for modules ) and finally our array of properties for creation.

For purposes of better visibility I have made sure there is the verbose switch added.

Once you execute this you should see output similar to the following :

2015-08-13_22h41_02

 

 

Prep work for resource functions

Now comes the most interesting part. Remember the important trio of functions I mentioned ? Get/Set/Test ? This will be the time to start coding them to do the really heavy lifting for us.

Now since I really like to simplify my life we will use a wrapper function to get and set settings in AppPool. Reason for doing this will be clearly visible later when we cut down the number of unnecessary code really significantly.

As usual the most up to date function you will find on GitHub

# Function which allows for quick set/get of app pool property - make sure you have the newest one 🙂
# available @  https://gist.github.com/RafPe/77d2ff28f7a0014bf0f1
function Invoke-AppPoolSetting
{

    param
    (    
        [string]$appPoolName,
        [Parameter(Mandatory=$true)]
        [ValidateNotNullOrEmpty()]
        $propertyName,
        [Parameter(Mandatory=$true)]
        [ValidateSet('get','set')]
        [string]$action,
        $value                
    )

    #Mapping of namespace (works on IIS 8.5)
    $NamespaceMapping = @{
       name                        = 'system.applicationHost/applicationPools/{0}'
       queueLength                 = 'system.applicationHost/applicationPools/{0}';
       autoStart                   = 'system.applicationHost/applicationPools/{0}';
       enable32BitAppOnWin64       = 'system.applicationHost/applicationPools/{0}';
       managedRuntimeVersion       = 'system.applicationHost/applicationPools/{0}';
       managedRuntimeLoader        = 'system.applicationHost/applicationPools/{0}';
       enableConfigurationOverride = 'system.applicationHost/applicationPools/{0}';
       managedPipelineMode         = 'system.applicationHost/applicationPools/{0}';
       CLRConfigFile               = 'system.applicationHost/applicationPools/{0}';
       passAnonymousToken          = 'system.applicationHost/applicationPools/{0}';
       startMode                   = 'system.applicationHost/applicationPools/{0}';
       identityType          = 'system.applicationHost/applicationPools/{0}/processModel';
       userName              = 'system.applicationHost/applicationPools/{0}/processModel';
       password              = 'system.applicationHost/applicationPools/{0}/processModel';
       loadUserProfile       = 'system.applicationHost/applicationPools/{0}/processModel';
       setProfileEnvironment = 'system.applicationHost/applicationPools/{0}/processModel';
       logonType             = 'system.applicationHost/applicationPools/{0}/processModel';
       manualGroupMembership = 'system.applicationHost/applicationPools/{0}/processModel';
       idleTimeout           = 'system.applicationHost/applicationPools/{0}/processModel';
       idleTimeoutAction     = 'system.applicationHost/applicationPools/{0}/processModel';
       maxProcesses          = 'system.applicationHost/applicationPools/{0}/processModel';
       shutdownTimeLimit     = 'system.applicationHost/applicationPools/{0}/processModel';
       startupTimeLimit      = 'system.applicationHost/applicationPools/{0}/processModel';
       pingingEnabled        = 'system.applicationHost/applicationPools/{0}/processModel';
       pingInterval          = 'system.applicationHost/applicationPools/{0}/processModel';
       pingResponseTime      = 'system.applicationHost/applicationPools/{0}/processModel';
       disallowOverlappingRotation    = 'system.applicationHost/applicationPools/{0}/recycling';
       disallowRotationOnConfigChange = 'system.applicationHost/applicationPools/{0}/recycling';
       logEventOnRecycle              = 'system.applicationHost/applicationPools/{0}/recycling';
       memory                         = 'system.applicationHost/applicationPools/{0}/recycling/periodicRestart';
       privateMemory                  = 'system.applicationHost/applicationPools/{0}/recycling/periodicRestart';
       requests                       = 'system.applicationHost/applicationPools/{0}/recycling/periodicRestart';
       time                           = 'system.applicationHost/applicationPools/{0}/recycling/periodicRestart';
       schedule                       = 'system.applicationHost/applicationPools/{0}/recycling/periodicRestart/schedule';
       loadBalancerCapabilities      = 'system.applicationHost/applicationPools/{0}/failure';
       orphanWorkerProcess           = 'system.applicationHost/applicationPools/{0}/failure';
       orphanActionExe               = 'system.applicationHost/applicationPools/{0}/failure';
       orphanActionParams            = 'system.applicationHost/applicationPools/{0}/failure';
       rapidFailProtection           = 'system.applicationHost/applicationPools/{0}/failure';
       rapidFailProtectionInterval   = 'system.applicationHost/applicationPools/{0}/failure';
       rapidFailProtectionMaxCrashes = 'system.applicationHost/applicationPools/{0}/failure';
       autoShutdownExe               = 'system.applicationHost/applicationPools/{0}/failure';
       autoShutdownParams            = 'system.applicationHost/applicationPools/{0}/failure';
       limit                     = 'system.applicationHost/applicationPools/{0}/cpu';
       action                    = 'system.applicationHost/applicationPools/{0}/cpu';
       resetInterval             = 'system.applicationHost/applicationPools/{0}/cpu';
       smpAffinitized            = 'system.applicationHost/applicationPools/{0}/cpu';
       smpProcessorAffinityMask  = 'system.applicationHost/applicationPools/{0}/cpu';
       smpProcessorAffinityMask2 = 'system.applicationHost/applicationPools/{0}/cpu';
       processorGroup            = 'system.applicationHost/applicationPools/{0}/cpu';
       numaNodeAssignment        = 'system.applicationHost/applicationPools/{0}/cpu';
       numaNodeAffinityMode      = 'system.applicationHost/applicationPools/{0}/cpu';
    }

    # Create target app pool name 
    if( [string]::IsNullOrEmpty($appPoolName) -or $appPoolName -eq 'applicationPoolDefaults' ) { $targetAppName = 'applicationPoolDefaults' } else { $targetAppName = [string]::Format("add[@name='{0}']",$appPoolName ) }
    
    switch ($action)
    {
      'get'        { 

                        Write-Debug "using $targetAppName as target name"
                        # gets value
                        
                        try
                        {
                          Write-Debug "Requesting property $propertyName value for appPool $appPoolName"
                          $res =  (Get-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST'  -filter $( $NamespaceMapping[ $propertyName ] -f $targetAppName ) -name $propertyName)
                            
                          Write-Debug "property is type of $($res.GetType().Name)"

                          #We need to distinguish from simple value type to complex value types
                          if ( $res.GetType().Name -eq 'ConfigurationAttribute')
                          {

                            return $res.Value.ToString()
                          }
                          else
                          {

                           
                            return $res.ToString()
                          }

                        }
                        catch
                        {
                            Throw "Could not retrieve property $propertyName for appPool $Name"
                        }
                        
                        
                   }
      
      'set'        { 
                       
                       if($null -eq $value)
                       {
                            Throw 'Please ensure that value is specified for set and is not NULL'
                       }

                        try
                        {
                          Write-Debug "Setting  property $propertyName with value $value for appPool $Name"
                          Set-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST'  -filter $( $NamespaceMapping[ $propertyName ] -f $targetAppName ) -name $propertyName -Value $value
                        
                        }
                        catch
                        {
                            Throw "Could not set property $propertyName with value $value for appPool $Name"
                        }

                       

                   }
    }
}

 

What does this allows me to do ? Well quite simple – in one cmdlet based on dynamic paramater name/value pair (or just parameter name) I can get or set a value for application pool! I think this is quite useful. So how this cmdlet is being used ?

Invoke-AppPoolSetting -propertyName disallowRotationOnConfigChange -action get;

The above quite easily gets me exactly the property I’m after. And now also the setting of property is quite intuitive. You just need to flip the correct action parameter and voilla – you are done for settign and getting in professional way.

 

Get-Resource

Its time to dive into functons that are responsible for actual heavy lifting of the resources. We will first focus on the Get function. It is quite simple as it just gets all resource properties which we have defined while creating this resource.  As usual the most up to date function wll be available on GitHub

function Get-TargetResource
{
    [CmdletBinding()]
    [OutputType([System.Collections.Hashtable])]
    param
    (
        [parameter(Mandatory = $true)]
        [System.String]
        $Name
    )

    #Remove debug from params
    $psboundparameters.Remove('Debug') | Out-Null
    $psboundparameters.Remove('Verbose') | Out-Null

    #TODO - schedule fix up 
    $psboundparameters.Remove('schedule') | Out-Null

    #Write-Verbose "Use this cmdlet to deliver information about command processing."

    #Write-Debug "Use this cmdlet to write debug information while troubleshooting."

    Write-Verbose 'Checking for app pool specified credentials'
    $appPoolUserName = Invoke-AppPoolSetting -propertyName userName -action get

    if($appPoolUserName)
    {
                Write-Verbose 'app pool has specific identity - creating PSCredential object'

                $AppPoolPassword = Invoke-AppPoolSetting -propertyName password -action get | ConvertTo-SecureString -AsPlainText -Force
                $AppPoolCred     = new-object -typename System.Management.Automation.PSCredential -argumentlist $appPoolUserName,$AppPoolPassword
    }
    else
    {
                Write-Verbose 'app pool does not have specific identity - nothing to do here'
                $AppPoolCred =$null
    }

    
    $returnValue = @{
        Name                          = Invoke-AppPoolSetting -propertyName Name -action get;
        queueLength                   = Invoke-AppPoolSetting -propertyName queueLength -action get;
        autoStart                     = Invoke-AppPoolSetting -propertyName autoStart -action get;
        enable32BitAppOnWin64         = Invoke-AppPoolSetting -propertyName enable32BitAppOnWin64 -action get;
        managedRuntimeVersion         = Invoke-AppPoolSetting -propertyName managedRuntimeVersion -action get;
        managedRuntimeLoader          = Invoke-AppPoolSetting -propertyName managedRuntimeLoader -action get;
        enableConfigurationOverride   = Invoke-AppPoolSetting -propertyName enableConfigurationOverride -action get;
        managedPipelineMode           = Invoke-AppPoolSetting -propertyName managedPipelineMode -action get;
        CLRConfigFile                 = Invoke-AppPoolSetting -propertyName CLRConfigFile -action get;
        passAnonymousToken            = Invoke-AppPoolSetting -propertyName passAnonymousToken -action get;
        startMode                     = Invoke-AppPoolSetting -propertyName startMode -action get;
        identityType                  = Invoke-AppPoolSetting -propertyName identityType -action get;
        userName                      = $appPoolUserName;
        Password                      = $AppPoolPassword;
        loadUserProfile               = Invoke-AppPoolSetting -propertyName loadUserProfile -action get;
        setProfileEnvironment         = Invoke-AppPoolSetting -propertyName setProfileEnvironment -action get;
        logonType                     = Invoke-AppPoolSetting -propertyName logonType -action get;
        manualGroupMembership         = Invoke-AppPoolSetting -propertyName manualGroupMembership -action get;
        idleTimeout                   = Invoke-AppPoolSetting -propertyName idleTimeout -action get;
        idleTimeoutAction             = Invoke-AppPoolSetting -propertyName idleTimeoutAction -action get;
        maxProcesses                  = Invoke-AppPoolSetting -propertyName maxProcesses -action get;
        shutdownTimeLimit             = Invoke-AppPoolSetting -propertyName shutdownTimeLimit -action get;
        startupTimeLimit              = Invoke-AppPoolSetting -propertyName startupTimeLimit -action get;
        pingingEnabled                = Invoke-AppPoolSetting -propertyName pingingEnabled -action get;
        pingInterval                  = Invoke-AppPoolSetting -propertyName pingInterval -action get;
        pingResponseTime              = Invoke-AppPoolSetting -propertyName pingResponseTime -action get;
        disallowOverlappingRotation   = Invoke-AppPoolSetting -propertyName disallowOverlappingRotation -action get;
        disallowRotationOnConfigChange = Invoke-AppPoolSetting -propertyName disallowRotationOnConfigChange -action get;
        logEventOnRecycle             = Invoke-AppPoolSetting -propertyName logEventOnRecycle -action get;
        memory                        = Invoke-AppPoolSetting -propertyName memory -action get;
        privateMemory                 = Invoke-AppPoolSetting -propertyName privateMemory -action get;
        requests                      = Invoke-AppPoolSetting -propertyName requests -action get;
        time                          = Invoke-AppPoolSetting -propertyName time -action get;
        schedule                      = '';# TODO - investigate schedule setup Invoke-AppPoolSetting -propertyName schedule -action get;
        loadBalancerCapabilities      = Invoke-AppPoolSetting -propertyName loadBalancerCapabilities -action get;
        orphanWorkerProcess           = Invoke-AppPoolSetting -propertyName orphanWorkerProcess -action get;
        orphanActionExe               = Invoke-AppPoolSetting -propertyName orphanActionExe -action get;
        orphanActionParams            = Invoke-AppPoolSetting -propertyName orphanActionParams -action get;
        rapidFailProtection           = Invoke-AppPoolSetting -propertyName rapidFailProtection -action get;
        rapidFailProtectionInterval   = Invoke-AppPoolSetting -propertyName rapidFailProtectionInterval -action get;
        rapidFailProtectionMaxCrashes = Invoke-AppPoolSetting -propertyName rapidFailProtectionMaxCrashes -action get;
        autoShutdownExe               = Invoke-AppPoolSetting -propertyName autoShutdownExe -action get;
        autoShutdownParams            = Invoke-AppPoolSetting -propertyName autoShutdownParams -action get;
        limit                         = Invoke-AppPoolSetting -propertyName limit -action get;
        action                        = Invoke-AppPoolSetting -propertyName action -action get;
        resetInterval                 = Invoke-AppPoolSetting -propertyName resetInterval -action get;
        smpAffinitized                = Invoke-AppPoolSetting -propertyName smpAffinitized -action get;
        smpProcessorAffinityMask      = Invoke-AppPoolSetting -propertyName smpProcessorAffinityMask -action get;
        smpProcessorAffinityMask2     = Invoke-AppPoolSetting -propertyName smpProcessorAffinityMask2 -action get;
        processorGroup                = Invoke-AppPoolSetting -propertyName processorGroup -action get;
        numaNodeAssignment            = Invoke-AppPoolSetting -propertyName numaNodeAssignment -action get;
        numaNodeAffinityMode          = Invoke-AppPoolSetting -propertyName numaNodeAffinityMode -action get;
    }

    $returnValue

}

 

Test-resource

This function is as the name says 😀 responsible for testing property/value So without any further delays here is the function

function Test-TargetResource
{
    [CmdletBinding()]
    [OutputType([System.Boolean])]
    param
    (
        [parameter(Mandatory = $true)]
        [System.String]
        $Name,

        [System.String]
        $queueLength,

        [System.String]
        $autoStart,

        [ValidateSet('true','false')]
        [System.String]
        $enable32BitAppOnWin64,

        [ValidateSet('v4.0','v2.0','')]
        [System.String]
        $managedRuntimeVersion,

        [System.String]
        $managedRuntimeLoader,

        [ValidateSet('true','false')]
        [System.String]
        $enableConfigurationOverride,

        [ValidateSet('Integrated','Classic')]
        [System.String]
        $managedPipelineMode,

        [System.String]
        $CLRConfigFile,

        [ValidateSet('true','false')]
        [System.String]
        $passAnonymousToken,

        [ValidateSet('AlwaysRunning','OnDemand')]
        [System.String]
        $startMode,

        [ValidateSet('ApplicationPoolIdentity','LocalSystem','LocalService','NetworkService','SpecificUser')]
        [System.String]
        $identityType,

        [System.String]
        $userName,

        [System.Management.Automation.PSCredential]
        $Password,

        [ValidateSet('true','false')]
        [System.String]
        $loadUserProfile,

        [ValidateSet('true','false')]
        [System.String]
        $setProfileEnvironment,

        [ValidateSet('LogonBatch','LogonService')]
        [System.String]
        $logonType,

        [ValidateSet('true','false')]
        [System.String]
        $manualGroupMembership,

        [System.String]
        $idleTimeout,

        [ValidateSet('Terminate','Suspend')]
        [System.String]
        $idleTimeoutAction,

        [System.String]
        $maxProcesses,

        [System.String]
        $shutdownTimeLimit,

        [System.String]
        $startupTimeLimit,

        [ValidateSet('true','false')]
        [System.String]
        $pingingEnabled,

        [System.String]
        $pingInterval,

        [System.String]
        $pingResponseTime,

        [ValidateSet('true','false')]
        [System.String]
        $disallowOverlappingRotation,

        [ValidateSet('true','false')]
        [System.String]
        $disallowRotationOnConfigChange,

        [System.String]
        $logEventOnRecycle,

        [System.String]
        $memory,

        [System.String]
        $privateMemory,

        [System.String]
        $requests,

        [System.String]
        $time,

        [Microsoft.Management.Infrastructure.CimInstance[]]
        $schedule,

        [ValidateSet('HttpLevel','TcpLevel')]
        [System.String]
        $loadBalancerCapabilities,

        [ValidateSet('true','false')]
        [System.String]
        $orphanWorkerProcess,

        [System.String]
        $orphanActionExe,

        [System.String]
        $orphanActionParams,

        [ValidateSet('true','false')]
        [System.String]
        $rapidFailProtection,

        [System.String]
        $rapidFailProtectionInterval,

        [System.String]
        $rapidFailProtectionMaxCrashes,

        [System.String]
        $autoShutdownExe,

        [System.String]
        $autoShutdownParams,

        [System.String]
        $limit,

        [ValidateSet('NoAcion','KillW3wp','Throttle','ThrottleUnderLoad')]
        [System.String]
        $action,

        [System.String]
        $resetInterval,

        [ValidateSet('true','false')]
        [System.String]
        $smpAffinitized,

        [System.String]
        $smpProcessorAffinityMask,

        [System.String]
        $smpProcessorAffinityMask2,

        [System.String]
        $processorGroup,

        [ValidateSet('MostAvailableMemory','WindowsScheduling')]
        [System.String]
        $numaNodeAssignment,

        [ValidateSet('Soft','Hard')]
        [System.String]
        $numaNodeAffinityMode
    )

    #Remove debug from params
    $psboundparameters.Remove('Debug') | Out-Null
    $psboundparameters.Remove('Verbose') | Out-Null

    #TODO - schedule fix up 
    $psboundparameters.Remove('schedule') | Out-Null

    $DesiredConfigurationMatch = $true

    # Check if WebAdministration module is present for IIS cmdlets
    if(!(Get-Module -ListAvailable -Name WebAdministration))
    {
        Throw 'Please ensure that WebAdministration module is installed.'
    }

    #Enumrate all params and info if necessary 
    foreach($psbp in $PSBoundParameters.GetEnumerator())
    {
          Write-Debug "xChecking value for $($psbp.Key)"

          $currValue = (Invoke-AppPoolSetting -propertyName $psbp.Key -action get)
          Write-Debug "Current value type :  $($currValue.GetType().Name)"
          Write-Debug "DSC type :  $($($psbp.Value).GetType().Name)"
          
          # We get current status and compare it with desired state  
          if( $currValue -ne $psbp.Value )
          {
                Write-Debug "Value for $( $psbp.Key ) [ $currValue ]  does not match the desired state [$( $psbp.Value )]"

                $DesiredConfigurationMatch = $false
          }
          else
          {
                 Write-Debug "Value for $( $psbp.Key ) [ $currValue ]  does match the desired state [$( $psbp.Value )]"
          }

    }

    return $DesiredConfigurationMatch
}

 

There are couple of points in this function wich are worth a bit of explanation. As I have seen several implementation of this function where suddenly you have ~ 150 conditional statements i.e. (if ($x) {} else {} ) and so on … so on and so on….. come on! Of course you can do better and optimize your code against redundant repetitions. Think always here about the moing parts … what is shared … what is common … what can you use …. 😀

So what can we use here ? Well something like $PsBoundParameters. If you would ask what are those asking the rigth places says … “It’s a hashtable containing the parameters that were bound and what values were bound to them” (source of this info )

We start by etablishing iteration of all available parameters

foreach($psbp in $PSBoundParameters.GetEnumerator())
{
    // ......

This will give us all parameters that have been passed to this function. Thats already great first step. So since this is testing function we need to start testing parameters passed . First we get current value using our customized function which we discussed before

$currValue = (Invoke-AppPoolSetting -propertyName $psbp.Key -action get)

This will retrieve current value for our specified setting. $psbp.Key will be holding name of parameter passes i.e. autostart , name etc.

Having that value we will compare it with our desired state. We do that by comparing $currValue with $psbp.Value (which is value passed as parameter)

# We get current status and compare it with desired state  
          if( $currValue -ne $psbp.Value )
          {
                Write-Debug "Value for $( $psbp.Key ) [ $currValue ]  does not match the desired state [$( $psbp.Value )]"

                $DesiredConfigurationMatch = $false
          }
          else
          {
                 Write-Debug "Value for $( $psbp.Key ) [ $currValue ]  does match the desired state [$( $psbp.Value )]"
          }

The last thing that is left is to return value which determines if we need to act. This is done by returning $DesiredConfigurationMatch

Ok so not much left to first success 😀 The remaining one is the one that does the work for us when we have drift from required configuration

Set-resource

We will apply similar approach as with our function above. Code looks following

function Set-TargetResource
{
    [CmdletBinding()]
    param
    (
        [parameter(Mandatory = $true)]
        [System.String]
        $Name,

        [System.String]
        $queueLength,

        [System.String]
        $autoStart,

        [ValidateSet('true','false')]
        [System.String]
        $enable32BitAppOnWin64,

        [ValidateSet('v4.0','v2.0','')]
        [System.String]
        $managedRuntimeVersion,

        [System.String]
        $managedRuntimeLoader,

        [ValidateSet('true','false')]
        [System.String]
        $enableConfigurationOverride,

        [ValidateSet('Integrated','Classic')]
        [System.String]
        $managedPipelineMode,

        [System.String]
        $CLRConfigFile,

        [ValidateSet('true','false')]
        [System.String]
        $passAnonymousToken,

        [ValidateSet('AlwaysRunning','OnDemand')]
        [System.String]
        $startMode,

        [ValidateSet('ApplicationPoolIdentity','LocalSystem','LocalService','NetworkService','SpecificUser')]
        [System.String]
        $identityType,

        [System.String]
        $userName,

        [System.Management.Automation.PSCredential]
        $Password,

        [ValidateSet('true','false')]
        [System.String]
        $loadUserProfile,

        [ValidateSet('true','false')]
        [System.String]
        $setProfileEnvironment,

        [ValidateSet('LogonBatch','LogonService')]
        [System.String]
        $logonType,

        [ValidateSet('true','false')]
        [System.String]
        $manualGroupMembership,

        [System.String]
        $idleTimeout,

        [ValidateSet('Terminate','Suspend')]
        [System.String]
        $idleTimeoutAction,

        [System.String]
        $maxProcesses,

        [System.String]
        $shutdownTimeLimit,

        [System.String]
        $startupTimeLimit,

        [ValidateSet('true','false')]
        [System.String]
        $pingingEnabled,

        [System.String]
        $pingInterval,

        [System.String]
        $pingResponseTime,

        [ValidateSet('true','false')]
        [System.String]
        $disallowOverlappingRotation,

        [ValidateSet('true','false')]
        [System.String]
        $disallowRotationOnConfigChange,

        [System.String]
        $logEventOnRecycle,

        [System.String]
        $memory,

        [System.String]
        $privateMemory,

        [System.String]
        $requests,

        [System.String]
        $time,

        [Microsoft.Management.Infrastructure.CimInstance[]]
        $schedule,

        [ValidateSet('HttpLevel','TcpLevel')]
        [System.String]
        $loadBalancerCapabilities,

        [ValidateSet('true','false')]
        [System.String]
        $orphanWorkerProcess,

        [System.String]
        $orphanActionExe,

        [System.String]
        $orphanActionParams,

        [ValidateSet('true','false')]
        [System.String]
        $rapidFailProtection,

        [System.String]
        $rapidFailProtectionInterval,

        [System.String]
        $rapidFailProtectionMaxCrashes,

        [System.String]
        $autoShutdownExe,

        [System.String]
        $autoShutdownParams,

        [System.String]
        $limit,

        [ValidateSet('NoAcion','KillW3wp','Throttle','ThrottleUnderLoad')]
        [System.String]
        $action,

        [System.String]
        $resetInterval,

        [ValidateSet('true','false')]
        [System.String]
        $smpAffinitized,

        [System.String]
        $smpProcessorAffinityMask,

        [System.String]
        $smpProcessorAffinityMask2,

        [System.String]
        $processorGroup,

        [ValidateSet('MostAvailableMemory','WindowsScheduling')]
        [System.String]
        $numaNodeAssignment,

        [ValidateSet('Soft','Hard')]
        [System.String]
        $numaNodeAffinityMode
    )

    #Remove debug from params
    $psboundparameters.Remove('Debug') | Out-Null
    $psboundparameters.Remove('Verbose') | Out-Null

    #TODO - schedule fix up 
    $psboundparameters.Remove('schedule') | Out-Null

    # Check if WebAdministration module is present for IIS cmdlets
    if(!(Get-Module -ListAvailable -Name WebAdministration))
        {
            Throw 'Please ensure that WebAdministration module is installed.'
        }

    #Enumrate all params and act if necessary 
    foreach($psbp in $PSBoundParameters.GetEnumerator())
    {
          Write-Debug "Checking value for $($psbp.Key)"
          
          # We get current status and compare it with desired state  
          if( (Invoke-AppPoolSetting -propertyName $psbp.Key -action get) -ne $psbp.Value )
          {
                Write-Verbose "Setting value for $( $psbp.Key ) to $( $psbp.Value )"
                Invoke-AppPoolSetting -propertyName $psbp.Key -value $psbp.Value -action set
          }

    }

    # Our dsc does not require reboot
    $global:DSCMachineStatus = 0


}

 

The whole magic is hidden in this pice of the code

          # We get current status and compare it with desired state  
          if( (Invoke-AppPoolSetting -propertyName $psbp.Key -action get) -ne $psbp.Value )
          {
                Write-Verbose "Setting value for $( $psbp.Key ) to $( $psbp.Value )"
                Invoke-AppPoolSetting -propertyName $psbp.Key -value $psbp.Value -action set
          }

In the ‘if’ statement we invoke get of setting (with use of our special function 🙂 ) and compare it with value passed. If there is mismatch we set appropiate value.

 

And that would be it for creating resource 🙂

 

Finally first testing 😀

So after all of that effort we have been through we can go ahead and give it a go 😀 I prepared a small example (which accordingly to best practices is part of the module )

This is what we will be playing with

2015-08-16_23h13_48

 

So lets configure it in our DSC config….

configuration Config_AppPoolDefaaults
{
    param
    (
        # Target nodes to apply the configuration
        [string[]]$NodeName = 'localhost'
    )

    # Import the module that defines custom resources
    Import-DscResource -ModuleName 'cWebAdmin'
    Import-DscResource –ModuleName ’PSDesiredStateConfiguration’

    Node $NodeName
    {
       
        # still pointing to defaults - to be changed 
        cWebAppPoolDefaults ConfigureAppPoolDefaults 
        {
            Name                =  'applicationPoolDefaults'        # by pecyfying 'applicationPoolDefaults' as name we show we want to act on default  
            managedPipelineMode =  'Integrated'            
            autoStart           =  'true'
            startMode           =  'AlwaysRunning'
        }
           
    }
}

 

Once invoked you will see similar output

PS C:\temp> Config_AppPoolDefaaults


    Directory: C:\temp\Config_AppPoolDefaaults


Mode                LastWriteTime         Length Name                          
----                -------------         ------ ----                          
-a----        16-8-2015     23:20           2142 localhost.mof                 

 

Then its the time to make that big cmdlet come to alive 😀 ….

 

Amongst many other reults (which I dont show here for clear of this post ) we see i.e.

2015-08-16_23h28_29

And later when setting is not as we wanted it … we see correction

2015-08-16_23h31_30

 

 

Results after applying in IIS ….

2015-08-16_23h32_24

 

 

 

Summary

I think this is great – power of DSC is really out there and I think we should be reaching out to a great tool like that! If you would have any comments or suggestions as usual please leave a comment or participate in this project on GitHub

In the next post we will be focusing on getting this up to level where you can drop it on production servers.  Stay tuned!

 

 

0

PowerShell – Automate multilanguage maintenance page with culture objects

maintenance …. doesnt that word just sounds like fun in the world of IT people 🙂 I think it all depends on you and how you set up your approach to the situation.

I recently have been overseeing maintenance where we had a single page for one of my customers. And that would be just fine and without a problem if not the fact within that single web page he had HTML containers for multiple languages and multiple countries.

Ok – but still ? Where is the problem with that ?  For purposes of ilustration I will try to come up with some text to give you an idea of how this would look like :

# -------- other code removed for visibility 

<p> On Monday August 17 2015 between 00:01 and 00:02 we will be undergoing maintenance.

Banana!
</p>

# --------- some other HTML here 

<p> El lunes 17 de agosto 2015 entre las 00:01 y las 00:02 estaremos experimentando mantenimiento.

¡Plátano! </p>

# --------- and again a lot of code here and there 

<p>Le lundi 17 Août 2015 0 heures 01-00h02 nous serons en cours de maintenance.

Banane! </p>


# --------- and again a lot of code here and there ... and so on and so on :O

 

Yey :O So I asked – ok how do you generate that file … unfortunately … this was a manual work which means someone would just go and modify those dates manually also using some google translate to get appropiate days/ months names.

 

We must automate this!

Yes! Cannot agree more that this cannot be like that. I’m fine with everlasting static code (altough I think the whole should be dynamically generated ) however lets start small 🙂

So what can do here …. we must identify moving parts. In our case the moving parts in first instance is the Country. Then we can have multiple different locale for country. Example ? Belgium … We can have English,French and German. Lastly we identify the property of locale like day of week/month etc

Now our code in source could look like

# -------- other code removed for visibility 

<p> On {US_en-US_DayOfWeek} {US_en-US_Month} {US_en-US_Day} {US_en-US_Year} between {US_en-US_StartDate} and {US_en-US_StopDate} we will be undergoing maintenance.

Banana!
</p>

# --------- some other HTML here 

<p> El {ES_es-ES_DayOfWeek} {ES_es-ES_Day} de {ES_es-ES_Month} {ES_es-ES_Year} entre las {ES_es-ES_StartDate} y las {ES_es-ES_StopDate} estaremos experimentando mantenimiento.

¡Plátano! </p>

# --------- and again a lot of code here and there 

<p>Le {FR_fr-FR_DayOfWeek} {FR_fr-FR_Day} {FR_fr-FR_Month} {FR_fr-FR_Year} {FR_fr-FR_StartDate}-{FR_fr-FR_StopDate} nous serons en cours de maintenance.

Banane! </p>


# --------- and again a lot of code here and there ... and so on and so on :O

 

So what have we done here ? Well we have introduced variables that will allow us to modify moving parts. If you look at single one {ES_es-ES_DayOfWeek}

We have it in ‘{}’ which will allow for quick search within content of files. Then we have it in capitols as country followed by locale and lastly by property name.

All of those divided by using ”_’ . Easy isnt?

 

Let the coding begins!

Since I want to avoid situation where I would have 50 ‘if statements’ or ‘replace’ statements in my code I will code with thinking of

  • modularity of the code
  • ease of extending this

 

Great! So now we have already prepared file contents with our customized variables and now we need to figure a way of putting that into the game 😀

 

So lets see what happened here

Here I have created my sel hashArray to be used within the script. As you can see the country is primary unique key for us. Each country can have multiple locales and maybe in future extra other settings.

# we define our countries
$pageCountries= @(    @{country="NL";[email protected]("nl-NL","en-US")}, `
                      @{country="BE";[email protected]("nl-BE","fr-BE","de-DE","en-US")},`
                      @{country="FR";[email protected]("fr-FR")},`
                      @{country="DE";[email protected]("de-DE")},`
                      @{country="UK";[email protected]("en-GB")},`
                      @{country="US";[email protected]("en-US")} 
)

 

Next I defined time slots for this maintanance. On purpose I used [datetime] object as I like the fact of just passing variables and not trying to parse from string 🙂 At the moment of writing duration is applied for all countries but as you can see it could be that we customize if for each country

# maintanance start date 
$maintananceStartDate=[datetime]::new(2015,8,16,1,0,0) # year,month,day,hour,minute,second

# maintanance duration (should be per country maybe ? )
[int]$maintananceDuration = 4

# stop time is 
$maintananceStopDate = $maintananceStartDate.AddHours($maintananceDuration)

 

Next we do iterations. We start off with countries and then for each of the countries we get into country locale :

# We start with each country
foreach($singleCountry in $pageCountries)
{
   
   # we then go for each locale
   foreach($languageLocale in $singleCountry.locale)
   {

 

From here we now will be creatign customzied variables for replacements. We start off by getting the locale culture for our current iteration

# get culture 
        $cultureCurrent = New-Object system.globalization.cultureinfo($languageLocale)

 

Having that we go ahead and create our properties and assign their values accordingly. If you notice later by just adding properties we will be auto-extending possible scope of variables in file ….

# We define our props 
        $props = @{ dayOfWeek           = $cultureCurrent.DateTimeFormat.DayNames[ [int]$maintananceStartDate.DayOfWeek ]; 
                    day                 = $maintananceStartDate.Day; 
                    month               = $cultureCurrent.DateTimeFormat.MonthNames[ $maintananceStartDate.Month ];
                    startTime           = $maintananceStartDate.ToShortTimeString();
                    stopTime            = $maintananceStopDate.ToShortTimeString();
                    datetime            = $maintananceStartDate.ToShortDateString()}

What is interesting here is that dayOfWeek comes from array of enums for specific language selected by integer value of our current day for maintanance.

DayNames looks as following in PS

DayNames                         : {Sunday, Monday, Tuesday, Wednesday...}

 

Cool – so lastly we go into replacing mode 🙂 As mentioned just a moment ago – with just adding single property into that array we get it auto added. This is done by iterating every named property in there

 # We need to now interate each of the props and make appropiate replacements
        foreach($item in $props.GetEnumerator()|select Name -ExpandProperty Name) 
        {

 

And then there is not much left except of just replacing those values

            $filter = "{" + [string]::Format('{0}_{1}_{2}',$singleCountry.country, $languageLocale, $item) + "}"

            Write-Host "Our filter is $filter" -ForegroundColor Yellow
            Write-Host "Target Value is $($props[ $item ] )" -ForegroundColor Yellow

            $maintanancePage = $maintanancePage.Replace( $filter, $props[ $item ] )

 

And thats IT ! Automated , streamlined , prone to user error 😀

Future todo

At the moment I think it is a good moment to have basic version working before we start rumbling with changes 😀 I defnitely will add Pester tests to it and make sure it can be more customized. Im thinking of per country advanced settigns maybe … ? We will see – will keep you updated.

 

 

 

0

Powershell – build complex module with functions dependency tree

logo-powershell-520x245Hey! So the time has come to share with you one of my recent achievements within automation. As you may have noticed in subejct of the post we will be focusing on complex functions with dependencies on onther functions within module. If that would not be enough … we will execute them remotely within session. Isnt that just uber cool ?

So in my last post I focuses on brief intro to what exporting of functions could look like. Today we start off with definition how we set up our functiosn and what we will use to build dependencies ( of course keeping it all under control )

How do you build complex functions ?

This might sound trivial however it is quite important to get this idea before you go ahead and build a module with 30 cmdlets and 3000 lines of code. From my DevOps experience I can tell you that wherever possible I start building from generic functions which then I use in more specific functions (a form of wrappers level above )

I think if we were to think of visualisation we could say we would get something in lines of :

2015-08-11_11h56_49

 

Someone just looking at it could say it looks really promising and professional 😀 Well it does. It is all about low level functions (the generic ones) to work more less with RAW data objects without performing complex validations. This validations and remaining high level functionality would be done by high level functions. 

 

Sample code which I could use for executing such dependent functions could look as follow :

                # Required functions on the remote host
                $exportNewObjGeneric  = Export-FunctionRemote New-ObjGeneric                                                                                                                                             
                $exportAddProps       = Export-FunctionRemote Add-ObjProperties 
                $exportGetAdSite      = Export-FunctionRemote Get-MyAdSite

                $remoteFunctions = @($exportGetAdSite, $exportNewObjGeneric, $exportAddProps)

Invoke-Command -Session $Session -ScriptBlock {   
            
                    # we recreate each of required functions
                    foreach($singleFunction in $using:remoteFunctions)
                    {
                        . ([ScriptBlock]::Create($singleFunction));
                    }
                 # ---- some other code doing magic -----
}

 

 

Great! Since it sounds so easy to implement … where is the catch …. 😀 Of course there is one … look what potentially could happen if you just go ahead and start referencing functions within a module ….

2015-08-11_15h59_13

 

It will not take a long time when you will lose track of what references what and where are your dependencies. This is just asking for trouble as you will practically not be able to assert what will be consequences of yoru changes on one end. I can guarantee you would get a butterfly effect in this scenario!

You will quickly lose ability to properly manage the code and automation will become more than a nightmare than a pleasure!

 

I have seen that to – however a bit differently. I though we could utilize something that every function of mine has – code based help!

Look at the example of function below – for purposes of this post I have ammended the code based help

2015-08-11_13h22_06

 

 

Have you noticed anything special ? …. Well if you didnt let me explain ….

 

Reference functions which are dependencies

Yes! This is the road to ultimate automation. I have came up with idea which can be describe as following :

  • Functions can nest (have a dependency  )  only on 1 level – so not dependency in dependency in dependency (but maybe you will come up with some more elegant way to overcome this 😀 )
  • Generic functions should not have dependencies on custom functions

With those 2 points I was able to continue with this solution. Therefore I have ammended a bit of the code we used last time and came up with the following :

 

Now this functions references one of which one already discussed called ‘Export-FunctionRemote’ (available @ Github ).

So what we got from the above ? Well we got something really great. In a controlled way by decorating our function with commented based help and specyfing  RequiredFunction<Some-FunctionName> it will be cosnidered as dependency in out script.

    <#
        .SYNOPSIS
        Do xyz 

        .FUNCTIONALITY
        RequiredFunction<Get-NobelPrice>
    #>

 

Finally use example

So we need to use what we have just received. I wont be taking long to explain – this is the pure awesomness of automation 🙂 …

                # Aqquire function name
                $functionName = $MyInvocation.MyCommand
             
                # get remote functions into remote script block
                Write-Verbose "Exporting functions for $functionName"
                $remoteFunctions = Get-RemoteRequiredFunctions -functionName                    $functionName

               

                Invoke-Command -Session $Session -ScriptBlock {   
            
                    # we recreate each of required functions
                    foreach($singleFunction in $using:remoteFunctions)
                    {
                        . ([ScriptBlock]::Create($singleFunction));
                    }

                    # ---- do magic -----
                }

 

 

Summary

I hope you like the idea of automating yoru functions in generic way to work with Raw data and then use high level functions to really utilize their potential. Given that you have also now received way to perform advanced operations by creating function dependencies.

Of course this is more than extendible – you can buld dependency trees – do more complex unit testign with Pester … there is not limtis 😀

Got feedback / issue ? As usual leave a comment or jump to GitHub / Gists

 

0

Powershell – DSC checklist

As you remember we are in the middle of the series for DSC module creation. I maybe should have mentioned this before but better now than never.  What I’m talking about … well its all about good practice within DSC. Some of them we do apply and some we will apply in our DSC series.

As it is good to have it around I decided to do a repost . Source from http://blogs.msdn.com/b/powershell/archive/2014/11/18/powershell-dsc-resource-design-and-testing-checklist.aspx

1       Resource module contains .psd1 file and schema.mof for every resource

2       Resource and schema are correct and have been verified using DscResourceDesigner cmdlets

3       Resource loads without errors

4       Resource is idempotent in the positive case

5       User modification scenario was tested

6       Get-TargetResource functionality was verified using Get-DscConfiguration

7       Resource was verified by calling Get/Set/Test-TargetResource functions directly

8       Resource was verified End to End using Start-DscConfiguration

9       Resource behaves correctly on all DSC supported platforms (or returns a specific error otherwise)

10     Resource functionality was verified on Windows Client (if applicable)

11     Get-DSCResource lists the resource

12     Resource module contains examples

13     Error messages are easy to understand and help users solve problems

14     Log messages are easy to understand and informative (including –verbose, –debug and ETW logs)

15     Resource implementation does not contain hardcoded paths

16     Resource implementation does not contain user information

17     Resource was tested with valid/invalid credentials

18     Resource is not using cmdlets requiring interactive input

19     Resource functionality was thoroughly tested

20     Best practice: Resource module contains Tests folder with ResourceDesignerTests.ps1 script

21     Best practice: Resource folder contains resource designer script for generating schema

22     Best practice: Resource supports -whatif

5

PowerShell – Local functions in remote sessions with ScriptBlock

logo-powershell-520x245 I think all of have been in situation when we wanted to run local PowerShell functions in remote sessions. If someone is new to the subject you would think there is no challenge here 😀

But wait …. there is 😀 I have personally seen couple of implementations of such ‘workarounds’ and in my opinion the most important is to choose the one that suits you.

Option 1 : Pass the function in script block :

You can directly invoke something in lines of :

Invoke-Command -ScriptBlock ${function:foo} -argumentlist "Bye!"

But when you do a lot of automations like I do this doesnt do any good for me :O

 

Option 2 : Use ScriptBlock

Script block you could say will be our ‘precious‘ 🙂 It seems a lot of ppl underestimate its potential. We should maybe first start of with how we define ScriptBlock.

I think easiest  to say would be something in lines of ‘… its a container for statements or/and expressions which can accept parameters and return value’ 

If you would like to know more please take a look at technet page.

Now you would ask but how this helps us in remote creation of PowerShell functions ? Ohhh it really does! It opens you doors to total automation ….

Look at this simple example (the snippet below comes from StackOverFlow )

$fooDef = "function foo { ${function:foo} }"

Invoke-Command -ComputerName someserver.example.com -ScriptBlock {
    Param( $fooDef )

    . ([ScriptBlock]::Create($using:fooDef))

    Write-Host "You can call the function as often as you like:"
    foo "Bye"
    foo "Adieu!"
}

 

So what happened here ? As we said a moment ago scriptblock is an container for expressions/statements and can accept input and return value(s)….  So we have defined our container of statements as

$fooDef = "function foo { ${function:foo} }"

Its perfectly valid to do so 😀

Next one is execution of our script in remote session and what we do here is we recreate our scriptblock based on definiton

. ([ScriptBlock]::Create($using:fooDef))

And voilla – from that moment you can use your function in remote session as it would be local!

 

One step further – automate exporting of functions

I’m a bit excited writing about this however using the above we will be able to create amazing automations. We will focus on creating multilayer (dependent each on) functions that we will automatically recreate when necessary.

But before we get there we need to automate exporting of our functions. For this we will use the following PS script :