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

0 comments

Azure Management Portal: Properly Remove Co-Adminstrators Permissions

Published on Wednesday, August 26, 2015 in ,

Something I’ve noticed for a while now: whenever I perform an Add-AzureAccount I see more subscriptions being returned than I’d expect. The list I have to choose from in the old portal (manage.windowsazure.com) is definitely not showing that much subscriptions. The new portal (portal.azure.com) displays also more subscriptions than I’d expect. The problem to sort those out is that many of those belong to subscriptions I’ve once have gotten access to, but now I no longer have. Either from customers or test subscriptions from colleagues.

For test purpose subscriptions I don’t really care whether people take my permissions away or not. But for production subscriptions I feel more at ease when I don’t have any permissions I don’t need anyway. Lately a customer mentioned my permissions were taken away, but I still saw their entry in the new Portal. Hmm, odd! Here’s how that’s possible:

First off, Initially I was granted access on my Microsoft Account (invisibal_at_gmail.com) through the old Portal:

image

Now I could manage that subscription through both old and new Portal.

image

And as I also worked for another “customer”, I had multiple subscriptions to manage, Setspn and RealDolmen Azure POC:

image

After my work was done, the customer removed me from the list of Administrators of the Setspn subscription.

subvs

su2

Now when I log in to the old Portal (manage.windowsazure.com) I’ll only see the other subscription.

image

However, when I log on to the new Portal, it’s still there!

image

Trying to show “all resources” of the Setspn subscription shows nothing. As expected.

image

The same is observed through PowerShell:

image

Now the only solution I could think is to also remove the live ID from the Azure Active Directory the subscription is linked to.

Capture3

Captur4e

After removing the user from the Azure AD, you’ll no longer see the subscription in the new Portal:

image

Well as you can see, not exactly… Typically when you try to reproduce things for screenshots, it doesn’t happen or it goes wrong. This is a case “it goes wrong”.  I tried a few times, but the GUID (belonging to the Azure AD I was part of) kept appearing… All I can say whenever the customer actually removed me from their Azure AD it got properly removed from my Azure Portal UI and PowerShell experience….

Conclusion:

I’m pretty sure the only reason you keeping seeing the entry in the new Portal is because you still have the User role assigned in the Azure Active Directory instance. So in a way you’re not really seeing the subscription, but rather the Azure Active Directory instance. But the issue remains the same, it clutters your PowerShell (get-AzureSubscription) and Portal UI experience. So whenever someone takes your co-administrator permissions away, ask them to also remove you from the Azure AD instance.

5 comments

MIM 2016: PowerShell Workflow and PowerShell v3

Published on Friday, August 21, 2015 in ,

One of the issues of running FIM 2010 R2 on Windows Server 2012 is calling PowerShell scripts from within FIM Portal Workflows (.NET). It seems the workflow code is running .NET 3.5 but uses PowerShell 2.0. When we started migrating our FIM 2010 to MIM 2016 (on Server 2012 R2) we ran into the same issues. This is the .NET code that has been running fine on Windows 2008 R2 for years without any issues:

RunspaceConfiguration config = RunspaceConfiguration.Create();
Runspace runspace = RunspaceFactory.CreateRunspace(config);
runspace.Open();
psh = PowerShell.Create();
psh.Runspace = runspace;
psh.AddCommand(this.PSCmdlet)

psh.Invoke();

And one the scripts that was executed contained code like this:

001
002
003
004
005
006
007
008

doSomething.ps1

#region Parameters
Param([string]$UserName,[string]$Department)
#endregion Parameters
Import-Module ActiveDirectory
Get-Aduser 
...

Now when porting that same logic to our MIM 2016 running on Windows Server 2016 we saw that our get-AD* cmdlets returned nothing. After some investigation we found the following error was triggered when running import-module Active Directory: The 'C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules\ActiveDirectory\ActiveDirectory.psd1' module cannot be imported because its manifest contains one or more members that are not valid. The valid manifest members are ('ModuleToProcess', 'NestedModules', 'GUID', 'Author', 'CompanyName', 'Copyright', 'ModuleVersion', 'Description', 'PowerShellVersion', 'PowerShellHostName', 'PowerShellHostVersion', 'CLRVersion', 'DotNetFrameworkVersion', 'ProcessorArchitecture', 'RequiredModules', 'TypesToProcess', 'FormatsToProcess', 'ScriptsToProcess', 'PrivateData', 'RequiredAssemblies', 'ModuleList', 'FileList', 'FunctionsToExport', 'VariablesToExport', 'AliasesToExport', 'CmdletsToExport'). Remove the members that are not valid ('HelpInfoUri'), then try to import the module again.

There are various topics online that cover this exact issue.

It seems some PowerShell modules are hardwired to require PowerShell v3. I came across the following suggestion a few times, but it scares me a bit as with my (limited?) knowledge of .NET It’s hard to estimate what impact this might have on FIM. The suggestion was to add the following to the Microsoft.ResourceManagement.Service.exe.config file.

001
002
003
004

<startup>
 <supportedRuntime version="v4.0"/>
 <supportedRuntime version="v2.0.50727"/>
</startup>

I found some approaches using a script that calls another script, but I wanted to avoid this. So I came up with the following approach to update the workflow itself:

PowerShellProcessInstance instance = new PowerShellProcessInstance(new Version(3, 0), null, null, false);
Runspace runspace = RunspaceFactory.CreateOutOfProcessRunspace(new TypeTable(new string[0]), instance);

Source:http://stackoverflow.com/questions/22383915/how-to-powershell-2-or-3-when-creating-runspace

The PowerShellProcessInstance is class that is available in System.Management.Automation. And that’s part of PowerShell itself. I tried various DLLS, but either they didn’t know the class or they resulted in the following error when building my .NET project:

The primary reference "System.Management.Automation" could not be resolved because it has an indirect dependency on the .NET Framework assembly "System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" which has a higher version "4.0.0.0" than the version "2.0.0.0" in the current target framework.    FODJ.FIM.Workflow.ActivityLibrary

My project is configured to build for .NET 3.5, but If I’m not mistaken .NET 3.5 use CLR 2.0. Whilst .net 4/4.5 use CLR 4.0 (see .NET Framework Versions and Dependencies). So I guess this route isn’t going to work after all. Back to the drawing board. As I only got a number of scripts to call like this, I decided to go back the wrapper script approach:

The script containing the logic to be executed:

001
002
003
004
005
006
007
008

doSomething.script.ps1

#region Parameters
Param([string]$UserName,[string]$Department)
#endregion Parameters
Import-Module ActiveDirectory
Get-Aduser 
...

As you can see I prepended .script to the .ps1 extension. And here’s my wrapper script. This is the one that is called from the FIM/MIM Workflow:

001
002
003
004
005
006

doSomething.ps1

Param
([string]$UserName,[string]$Department)
$script = $myinvocation.invocationName.replace(".ps1",".script.ps1")
powershell -version 3.0 -file $script -UserName $username -Department 
$Department

There are some things to note: the param line is just a copy past from the base script. And I just specify them as parameters again when calling the base script. I had been looking for a way to use unbound parameters, e.g. the calling workflow says –username … –department … and the wrapper script just passes it over. That would have allowed me to have a generic wrapper script. I got pretty close to getting it to work, but I kept running into issues. In the end I just decided to go for KISS.

Note: if you want to capture errors like the one I show from “import-module Active Directory”, just use the $error variable. You can use it like this. Saving it to disk is just one example. Typically you could integrate this with your logging function.

001
002
003

$error.Clear
Import-Module ActiveDirectory
$error | out-file c:\users\public\error.txt

10 comments

Azure Quick Tip: Block or Allow ICMP using Network Security Groups

Published on Tuesday, August 18, 2015 in ,

For a while now Azure allows administrators to restrict network communications between virtual machines in Azure. Restrictions can be configured through the use of Network Security Groups (NSGs). Those can be linked to both subnets or virtual machines. Check the following link if you want some more background information: https://azure.microsoft.com/en-us/documentation/articles/virtual-networks-nsg/

A NSG always contains some default rules. By default all outbound traffic is allowed, and inbound from other subnets (not the internet) is also allowed. Typically if you ping between VM’s on different subnets (same VNET) you’ll see that the machines respond as expected.

Now what if you want to restrict traffic between subnets but still allow ICMP? ICMP is great for troubleshooting connectivity. Set-AzureNetworkSecurityRule allows you to provide the protocol parameter. In a typical firewall scenario this value would contain TCP, UDP, ICMP, … Ping uses ICMP which is neither TCP or UDP… Azure only seem to allow TCP, UDP and * for the protocol:

image

Now how can we block all traffic but allow ICMP? Simple, by explicitly denying UDP and TCP but allowing *. In this example I included the allow rule, but it should be covered by the default rules anyhow.

001
002
003
004
005
006

#allow ping, block UDP/TCP
Get-AzureNetworkSecurityGroup -name "NSG-1" | Set-AzureNetworkSecurityRule -Name BlockTCP -Type Inbound -Priority 40000 -Action Deny -SourceAddressPrefix "*"  -SourcePortRange '*' -DestinationAddressPrefix '*' -DestinationPortRange '*' -Protocol "TCP"

Get-AzureNetworkSecurityGroup -name "NSG-1" | Set-AzureNetworkSecurityRule -Name BlockUDP -Type Inbound -Priority 40001 -Action Deny -SourceAddressPrefix "*"  -SourcePortRange '*' -DestinationAddressPrefix '*' -DestinationPortRange '*' -Protocol "UDP"

Get-AzureNetworkSecurityGroup -name "NSG-1" | Set-AzureNetworkSecurityRule -Name AllowPing -Type Inbound -Priority 40002 -Action Allow -SourceAddressPrefix "*"  -SourcePortRange '*' -DestinationAddressPrefix '*' -DestinationPortRange '*' -Protocol "*"

If we want to work the other way round: allow UDP/TCP but block ICMP we can turn the logic around:

001
002
003
004
005
006

#block ping, allow UDP/TCP
Get-AzureNetworkSecurityGroup -name "NSG-1" | Set-AzureNetworkSecurityRule -Name AllowTCP -Type Inbound -Priority 40000 -Action Allow -SourceAddressPrefix "*"  -SourcePortRange '*' -DestinationAddressPrefix '*' -DestinationPortRange '*' -Protocol "TCP"

Get-AzureNetworkSecurityGroup -name "NSG-1" | Set-AzureNetworkSecurityRule -Name AllowUDP -Type Inbound -Priority 40001 -Action Allow -SourceAddressPrefix "*"  -SourcePortRange '*' -DestinationAddressPrefix '*' -DestinationPortRange '*' -Protocol "UDP"

Get-AzureNetworkSecurityGroup -name "NSG-1" | Set-AzureNetworkSecurityRule -Name BlockPing -Type Inbound -Priority 40002 -Action Deny -SourceAddressPrefix "*"  -SourcePortRange '*' -DestinationAddressPrefix '*' -DestinationPortRange '*' -Protocol "*"

The source/destination information is pretty open as I use * for those, but that’s just an example here. It’s up to you to decide for which ranges to apply this. And you might probably open up some additional ports for actual traffic to be allowed. The above logic is also mentioned in the information I linked at the beginning of the article:

The current NSG rules only allow for protocols ‘TCP’ or ‘UDP’. There is not a specific tag for ‘ICMP’. However, ICMP traffic is allowed within a Virtual Network by default through the Inbound VNet rules that allow traffic from/to any port and protocol ‘*’ within the VNet.

Kudos to my colleague Nichola (http://www.vnic.be) for taking the time to verify this.

0 comments

Azure DSC and Configuration Archive Case Sensitiveness

Published on Monday, June 29, 2015 in , ,

Lately I’ve been working on my Azure Automation skills. More precisely I want to have a script that is able to create a virtual machine and creates a new Active Directory (domain controller) on it. The are several ways of doing this. One way is to create a PowerShell script that is executed through the Azure script extension. An other way is through the Desired State Configuration (DSC) extension. In my opinion the latter is the best option. DSC is really great at getting your server configured with minimal scripting. If you’re unfamiliar with DSC you might be experiencing quite some issues in the beginning. Having a working DSC extension is one thing, but getting it to work through the Azure DSC extension has it’s own challenges. Most of these so called issues have probably to do with me being at the bottom of the DSC learning curve…

A while back I wrote a simple DSC extension to get the time zone right (Working with PowerShell DSC and Azure VM’s based on Windows 2012). That simple example went pretty well. Now I wasn’t even getting my DSC script to properly download to the target system. Now how on earth could something that simple be that hard? Here’s the error I was having:

Log file location: C:\WindowsAzure\Logs\Plugins\Microsoft.Powershell.DSC\1.10.1.0\DscExtensionHandler.3.20150627-211133

VERBOSE: [2015-06-27T21:11:42] File lock does not exist: begin processing
VERBOSE: [2015-06-27T21:11:42] File
C:\Packages\Plugins\Microsoft.Powershell.DSC\1.10.1.0\bin\..\DSCWork\2-Completed.Install.dsc exists; invoking extension
handler...
VERBOSE: [2015-06-27T21:11:43] Reading handler environment from
C:\Packages\Plugins\Microsoft.Powershell.DSC\1.10.1.0\bin\..\HandlerEnvironment.json
VERBOSE: [2015-06-27T21:11:44] Reading handler settings from
C:\Packages\Plugins\Microsoft.Powershell.DSC\1.10.1.0\RuntimeSettings\3.settings
VERBOSE: [2015-06-27T21:11:47] Applying DSC configuration:
VERBOSE: [2015-06-27T21:11:47]     Sequence Number:              3
VERBOSE: [2015-06-27T21:11:47]     Configuration Package URL:   
https://thvuystoragetest.blob.core.windows.net/windows-powershell-dsc/MyDC.ps1.zip
VERBOSE: [2015-06-27T21:11:47]     ModuleSource:                
VERBOSE: [2015-06-27T21:11:47]     Configuration Module Version:
VERBOSE: [2015-06-27T21:11:47]     Configuration Container:      MyDC.ps1
VERBOSE: [2015-06-27T21:11:47]     Configuration Function:       MyDC (2 arguments)
VERBOSE: [2015-06-27T21:11:47]     Configuration Data URL:      
https://thvuystoragetest.blob.core.windows.net/windows-powershell-dsc/MyDC-69d57a1f-2522-41d7-b5ac-3b635c63ba93.psd1
VERBOSE: [2015-06-27T21:11:47]     Certificate Thumbprint:       FC89BDBF395EFC39EA3633BBDEAE9BB7AA7C475E
VERBOSE: [2015-06-27T21:11:47] Creating Working directory:
C:\Packages\Plugins\Microsoft.Powershell.DSC\1.10.1.0\bin\..\DSCWork\MyDC.ps1.3
VERBOSE: [2015-06-27T21:11:48] Downloading configuration package
VERBOSE: [2015-06-27T21:11:48] Downloading
https://thvuystoragetest.blob.core.windows.net/windows-powershell-dsc/MyDC.ps1.zip?sv=2014-02-14&sr=b&sig=k38XoVn5%2Bn5P1UIMM8q
mh9bc7YBD7Q5ZNV%2B5aqvP2xs%3D&se=2015-06-27T20%3A10%3A16Z&sp=rd to
C:\Packages\Plugins\Microsoft.Powershell.DSC\1.10.1.0\bin\..\DSCWork\MyDC.ps1.3\MyDC.ps1.zip
VERBOSE: [2015-06-27T21:11:48] An error occurred processing the configuration package; removing
C:\Packages\Plugins\Microsoft.Powershell.DSC\1.10.1.0\bin\..\DSCWork\MyDC.ps1.3
VERBOSE: [2015-06-27T21:11:48] [ERROR] An error occurred downloading the Azure Blob: Exception calling "DownloadFile" with "2"
argument(s): "The remote server returned an error: (404) Not Found."
The Set-AzureVMDscExtension cmdlet grants access to the blobs only for 1 hour; have you exceeded that interval?
VERBOSE: [2015-06-27T21:11:49] Writing handler status to C:\Packages\Plugins\Microsoft.Powershell.DSC\1.10.1.0\Status\3.status
VERBOSE: [2015-06-27T21:11:49] Removing file lock


The most interesting part:

VERBOSE: [2015-06-27T21:11:48] [ERROR] An error occurred downloading the Azure Blob: Exception calling "DownloadFile" with "2"
argument(s): "The remote server returned an error: (404) Not Found."
The Set-AzureVMDscExtension cmdlet grants access to the blobs only for 1 hour; have you exceeded that interval?

I found the following URL from the log file: https://thvuystoragetest.blob.core.windows.net/windows-powershell-dsc/MyDC.ps1.zip?sv=2014-02-14&sr=b&sig=k38XoVn5%2Bn5P1UIMM8qmh9bc7YBD7Q5ZNV%2B5aqvP2xs%3D&se=2015-06-27T20%3A10%3A16Z&sp=rd

Some googling led me to some results, but nothing relevant. I took the URL and copy pasted into a browser:

StorageContainerXMLChrome

It showed me an XML type response stating: BlobNotFound: The specified blob does not exist. By accident I used an open chrome instance as I typically use IE. If I visited this URL using IE I simply got a page not found error. That’s probably something that can be tweaked in the IE settings, but still good to know. After seeing that error page I went to the Azure management portal:

StorageContainer

I drilled down till I found my .ps1.zip file and copy pasted its URL in a notepad++ windows:

image

As you can see the only difference is the casing of “MyDC.ps1”… The URL in the log file is constructed by the Azure DSC extension. More particular by the following PowerShell lines:

001
002
003
004
005
006
007
008
009
010
011
012

$configurationArchive = "MyDC.ps1.zip"
$configurationName = "MyDC" 
$configurationData = "C:\Users\Thomas\SkyDrive\Documenten\Work\Blog\DSC\Final\myDC.psd1"

$VM = get-AzureVM -Service $svcname -name 
$vmname
$vm
 = Set-AzureVMDSCExtension -VM $vm `
    -ConfigurationArchive $configurationArchive `
    -ConfigurationName $configurationName `
    -ConfigurationArgument $configurationArguments `
-ConfigurationDataPath 
$configurationData

$vm
 | update-azurevm

Updating my $configurationArchive to myDC.ps1.zip was all I needed to do to get this baby running.

Summary

Whenever creating storage accounts, containers or blobs on them, make sure to watch out for case sensitiveness. In my opinion using an all lower case approach might be the best way forward.

0 comments

Working with PowerShell DSC and Azure VM’s based on Windows 2012

Published on Wednesday, June 17, 2015 in ,

Mostly when I work with Azure VM’s I do the actual VM creation using Azure PowerShell cmdlets. I like how you can have some template scripts that create VM’s from beginning to end. Typically I create a static IP reservation, I join them to an AD domain, I add one or more additional disks,I add the Microsoft Antimalware extension…. When the VM is provisioned I log on and I’m pretty much ready to go. One of the things I noticed is that the Time Zone was set to UTC where I like it to be GMT+1. Obviously this only requires two clicks but I wanted this to be done for me. Now there are various approaches: either us traditional tooling like SCCM, GPO (is there a setting/registry key? ), …. or do it the Azure way. As far as Azure is concerned I could create a custom VM image or use PowerShell DSC (Desired State Configuration).

I prefer DSC over a custom image. The main reason is that I can apply these DSC customizations to whatever image from the gallery I feel like applying them. If the SharePoint team wants to take the latest SharePoint image from the gallery, I can just apply my DSC over it. If there’s a more recent Windows 2012 R2 image, I can just throw my DSC against it and I’m ready to go.

The following example shows how to apply a given DSC configuration to a VM.

001
002
003
004
005

$configurationArchive = "DSC_SetTimeZone.ps1.zip"
$configurationName = "SetTimeZone"
$VM = Get-AzureVM -ServiceName "contoso-svc" -Name "CONTOSO-SRV"
$VM = Set-AzureVMDSCExtension -VM $vm -ConfigurationArchive $configurationArchive -ConfigurationName 
$configurationName
$VM
 | update-AzureVM

Now I wont go into all of the details, but here are some things I personally ran into.

Creating and Uploading the DSC configuration archive

Initially I had some trouble wrapping my head around how to get my script to run on a target machine. I had this cool DSC script I found on the internet and tweaked it a bit:

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022

#Requires -version 4.0
Configuration SetTimeZone
{
    Param
    (
        #Target nodes to apply the configuration
        [Parameter(Mandatory = $false)]
        [ValidateNotNullorEmpty()]
        [String]$SystemTimeZone="Romance Standard Time"
    )

    Import-DSCResource -ModuleName xTimeZone

    Node localhost
    {
        xTimeZone TimeZoneExample
        {
            TimeZone = $SystemTimeZone
        }
    }
}

This script depends on the xTimeZone DSC resource. As I already knew, those DSC resources, like xTimeZone, come in waves. Would my server have the latest version? Did I have to install that out of band? It seems not. All you need to do is create a configuration archive, a ZIP file, which contains both your script and the resource it depends on. The Azure cmdlets are an easy way to do this. They’ll also make sure all the dependent DSC resources are added to the package.

We got our script in c:\users\thomas\onedrive\documenten\work\blog\DSC. Some steps further I’ll get you the information where to store the DSC resources.

DSC_1

By using the following command we can create and upload the package to the “setspn” storage account:

001
002
003
004

$subscriptionID = "af2f6ce8-e4f3-abcd-abcd-34ab4ce9c7d3"
$storageAccountName = "setspn"
Set-AzureSubscription -SubscriptionId $subscriptionID -CurrentStorageAccount $storageAccountName
Publish-AzureVMDscConfiguration -ConfigurationPath C:\Users\Thomas\OneDrive\Documenten\Work\Blog\DSC\DSC_SetTimeZone.ps1

We need to execute this from an Azure PowerShell prompt. I executed this command from a Windows 8.1 machine that is running PowerShell v4.

DSC_2

It seems to be complaining that we are running this from an x86 prompt instead of an x64 prompt. But the Azure PowerhShell prompt is an x86 prompt… The error in words:

Publish-AzureVMDscConfiguration : Configuration script 'C:\Users\Thomas\SkyDrive\Documenten\Work\Blog\DSC\DSC_SetTimeZo
ne.ps1' contained parse errors:
At C:\Users\Thomas\SkyDrive\Documenten\Work\Blog\DSC\DSC_SetTimeZone.ps1:2 char:1
+ Configuration SetTimeZone
+ ~~~~~~~~~~~~~
Configuration is not supported in a Windows PowerShell x86-based console. Open a Windows PowerShell x64-based console,
and then try again.
At C:\Users\Thomas\SkyDrive\Documenten\Work\Blog\DSC\DSC_SetTimeZone.ps1:3 char:1
+ {
+ ~
Unexpected token '{' in expression or statement.
At C:\Users\Thomas\SkyDrive\Documenten\Work\Blog\DSC\DSC_SetTimeZone.ps1:21 char:1
+ }
+ ~
Unexpected token '}' in expression or statement.
At line:1 char:1

The error is quite misleading. I tried the various “DSC” cmdlets, like Get-DSCResource, and they all failed saying that the cmdlet could not be found. So it seems I needed the WMF framework to be installed. Shame on me. Here’s some explanation towards the prerequisites: TechNet Gallery: DSC Resource Kit (All Modules) Using the WMF 5.0 installer got me further.

DSC_2b

Now off to creating the package again: again an error…

DSC_3

Now it seems to complain it can’t find the DSC resources… But I installed them?! The error in words:

VERBOSE: Parsing configuration script: C:\Users\Thomas\SkyDrive\Documenten\Work\Blog\DSC\DSC_SetTimeZone.ps1
VERBOSE: Loading module from path 'C:\Program Files
(x86)\WindowsPowerShell\Modules\xTimeZoneSource\DSCResources\xTimeZone\xTimeZone.psm1'.
Publish-AzureVMDscConfiguration : Configuration script 'C:\Users\Thomas\SkyDrive\Documenten\Work\Blog\DSC\DSC_SetTimeZo
ne.ps1' contained parse errors:
At C:\Users\Thomas\SkyDrive\Documenten\Work\Blog\DSC\DSC_SetTimeZone.ps1:16 char:9
+         xTimeZone TimeZoneExample
+         ~~~~~~~~~
Undefined DSC resource 'xTimeZone'. Use Import-DSCResource to import the resource.
At line:1 char:1

After some googling I found out that the PowerShell prompt imports to module it finds in it’s PATH variable. As we are running from an x86 prompt, the folder that was loaded was different. Typically all DSC guides tell you to install DSC resources below C:\Program Files\WindowsPowerShell\Modules but in fact for the Azure PowerShell prompt you need to put them in C:\Program Files (x86)\WindowsPowerShell\Modules or you have to modify your PATH variable to include the x64 location…. I choose to copy the module to the x86 location:

DSC_3c

And all seems fine now:

DSC_4

Applying the DSC to a Windows 2012 VM

The DSC script I created worked fine on a newly install Windows 2012 R2 VM, but on a Windows 2012 the extension seemed to have troubles. Now that wasn’t supposed to happen… The good thing about the Azure DSC extension is that the logging is quite decent. Inside the VM you can find some log files in the following location: C:\WindowsAzure\Logs\Plugins\Microsoft.Powershell.DSC\1.10.1.0

The following extract comes from the DscExtensionHandler log file:

VERBOSE: [2015-05-28T21:43:17] Applying DSC configuration:
VERBOSE: [2015-05-28T21:43:17]     Sequence Number:              0
VERBOSE: [2015-05-28T21:43:17]     Configuration Package URL:   
https://setspn.blob.core.windows.net/windows-powershell-dsc/DSC_SetTimeZone.ps1.zip
VERBOSE: [2015-05-28T21:43:17]     ModuleSource:                
VERBOSE: [2015-05-28T21:43:17]     Configuration Module Version:
VERBOSE: [2015-05-28T21:43:17]     Configuration Container:      DSC_SetTimeZone.ps1
...
VERBOSE: [2015-05-28T21:44:27] [ERROR] Importing module xTimeZone failed with error - File C:\Program
Files\WindowsPowerShell\Modules\xTimeZone\DscResources\xTimeZone\xTimeZone.psm1 cannot be loaded because running scripts is
disabled on this system. For more information, see about_Execution_Policies at http://go.microsoft.com/fwlink/?LinkID=135170.

Now that’s a pretty well known message… Bummer. Seems the execution policy on the Windows 2012 machine is set to restricted. Now there’s a way around that. Scripts could be executed with the option “-executionpolicy bypass”. But we can’t control that as the DSC extension is responsible for this. Kind of a bummer. The Windows 2012 R2 image seems to have RemoteSigned as default execution policy…

Now this got me curious. Would the run PowerShell script extension also suffer from this? If it would not, I could have a small PowerShell script execute first that alters the execution policy!

001
002

Set-ExecutionPolicy remotesigned

I create a PowerShell script with this line in it. Saved it to disk and then used AzCopy to copy it a container in a storage account. Executing the script:

DSC_ExecScript

After executing I can confirm that the execution policy has changed:

ExecPol

The logging for this extension can be found here: C:\WindowsAzure\Logs\Plugins\Microsoft.Compute.CustomScriptExtension\1.4 From the log file we can see that the PowerShell script extension run scripts in a more robust way:

2015-05-28T21:36:58.7646763Z    [Info]:    HandlerSettings = ProtectedSettingsCertThumbprint: , ProtectedSettings: {}, PublicSettings: {FileUris: [https://storaccount.blob.core.windows.net/windows-powershell-dsc/test.ps1?sv=2014-02-14&sr=b&sig=eijcTn9I2kWuOPU1CK%2F9zQ3tAO1NIUrs8wT2gUE8z0o%3D&se=2015-05-29T21%3A07%3A00Z&sp=r], CommandToExecute: powershell -ExecutionPolicy Unrestricted -file test.ps1 }

As you can see this script is called while specifying the execution policy. Now we’ll be able to apply our DSC extension

DSC_ExecDSC

And the log file contents:

VERBOSE: [2015-05-29T00:00:00] Import script as new module:
C:\Packages\Plugins\Microsoft.Powershell.DSC\1.10.1.0\bin\..\DSCWork\DSC_SetTimeZone.ps1.1\DSC_SetTimeZone.ps1
...
VERBOSE: [2015-05-29T00:00:12] Executing Start-DscConfiguration...
...
VERBOSE: [2015-05-29T00:00:20] [SRV2012]: LCM:  [ Start  Set      ]
VERBOSE: [2015-05-29T00:00:22] [SRV2012]: LCM:  [ Start  Resource ]  [[xTimeZone]TimeZoneExample]
VERBOSE: [2015-05-29T00:00:22] [SRV2012]: LCM:  [ Start  Test     ]  [[xTimeZone]TimeZoneExample]
VERBOSE: [2015-05-29T00:00:22] [SRV2012]: LCM:  [ End    Test     ]  [[xTimeZone]TimeZoneExample]  in 0.1100 seconds.
...

Conclusion

Configuring the time zone using DSC might be a bit overkill. But it’s an excellent exercise to get the hang of this DSC stuff. For a good resource on DSC check this tweet from me. I myself plan to create more DSC scripts in the near future. I tear VM’s up and down all the time. I would love to have a DSC that creates me a Windows AD domain, a Microsoft Identity Manager installation, a ….

6 comments

Commvault REST API using PowerShell

Published on Sunday, April 26, 2015 in ,

A while ago I wrote a blog post about Connecting to the 3PAR WebAPI using PowerShell. Today I’m doing the same but now with the Commvault REST API. Connecting to that API is even easier! Commvault has a nice sandbox to get familiar with the REST API! This one is definately worth a look. It can be accessed at http://commvaultwebconsoleserver.contoso.com/webconsole/sandbox/ It looks like this:

2015-02-27_10-29-35_CommvaultRestAPI

Working with the API is similar to the 3PAR approach: first we authenticate and get a token. Then we use that token to call other services. In order to get this working I just looked at the c# sample code Commvault provides: Commvault Documentation: REST API - Getting Started Using C# Here’s how you can authenticate using PowerShell:

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015

#Credentials
$username = "Contoso\s_apiuser" 
$password = P@$$w0rd 
#Commvault web console server
$SERVER = "srdccvws0001.rddcmgmt.local" 
#API URL
$APIURL = "http://$($SERVER):81/SearchSvc/CVWebService.svc" 


$APIURLaction = "$APIURL/login" 
$passwordB64byte = [System.Text.Encoding]::UTF8.GetBytes($password) 
$encodedPassword = [System.Convert]::ToBase64String($passwordB64byte) 
$loginReq = "<DM2ContentIndexing_CheckCredentialReq mode=""Webconsole"" username=""$username"" password=""$encodedPassword"" />" 
$result = Invoke-WebRequest -Uri $APIURLaction -Method POST -Body $loginReq -UseBasicParsing       
$token = (([xml]$result.content).SelectSingleNode("/DM2ContentIndexing_CheckCredentialResp/@token")).value

The code might seem cryptic but is actually pretty straightforward: we need to call the /login service and pass it some parameters.The service requires the password to be base64 encoded. As you might guess this information goes across the wire… So I would definitely advise to use an account that only has permissions to the items it needs to access. Ideally we’d approach the service over SSL but I’m not sure if Commvault allows SSL to be configured for the services.

The username and encoded password have to be passed in a specific XML string that is passed as the body of the web request. The result is an XML string that contains a token value. We don’t need the whole string but only the part representing the token. Now that we managed to get a token we can start calling specific services. If I'm not mistaken this token will remain valid until there’s a period of inactivity (30’).

Here’s how you can get all Clients:

001
002
003
004
005
006
007
008
009
010

$service = "/client" 
$action = "GET" 
$APIURLaction = "$APIURL$service" 
$headers = @{} 
#default is XML
$headers["Accept"] = "application/json" 
$headers["Authtoken"] = $token 
$result = Invoke-WebRequest -Uri $APIURLaction -Headers $headers -Method $action -UseBasicParsing 
$clientInfo = ($result.content | ConvertFrom-Json).App_GetClientPropertiesResponse.clientProperties
 

If you go through the REST API documentation you’ll notice that some services are GET based and other are POST based. The /client service is GET based, the /jobdetails is POST based. You’ll also notice that we pass an XML string as the body for the request.

001
002
003
004
005
006

$jobID = "3040" 
$body = "<JobManager_JobDetailRequest jobId=""$jobID""/>" 
$service =  "/JobDetails" 
$action = "POST" 
$result = Invoke-WebRequest -Uri $APIURLaction -Headers $headers -Method $action -UseBasicParsing -Body $body 
$jobDetails = ($result.content | ConvertFrom-Json).JobManager_JobDetailResponse.job.jobDetail

I hope with these basic examples you can now successfully connect yourself. As you might see, the above code contains no error handling. Depending on how you will use scripts like that I would advise to add some logging and error handling. Try providing wrong credentials or entering a bad servername and see what happens.