1/12/2011

64bit Windows環境で System.Management.Automation を使用してAzure Management CommandLets を利用する

前回のブログで 「64nit Windows環境において、C#からAzure Management CommandLets を利用できない」と書きましたが、皆さんは動きましたか?動かない方はADD-PSSnapinコマンドを実行するところで失敗していると思います。

これは System.Management.AutomationがC:\Program Files (x86)フォルダ配下に存在する物を使用している場合におこる現象です。利用しているクラスは.NETの32bit向けのクラス提供となっており、Azure Management Tools CommandLets のインストーラが.NETの64bit環境にしかインストールしないため、.NETの32bitのモジュールからは使うことができないのです。試にWindows PowerShell(x86)を実行していただくとわかると思いますが、ADD-PSSnapinコマンドでAzure Management Tools CommandLetsを利用することができません。

これを回避するには2つの方法があります。64bitに対応したWindows SDKをインストールする方法と、Azure Management Tools CommandLetsを32bitのPowershellにもインストールする方法の2種類です。おすすめとしては前者の64bitに対応したWindows SDKをインストールする方法です。

1. 64bitに対応したWindows SDKをインストールする方法

下記のサイトからインストーラをダウンロードして実行してください。

Windowsデベロッパーセンター Windows SDK
http://msdn.microsoft.com/ja-jp/windows/bb980924

Microsoft Windows SDK for Windows 7 and .NET Framework 4
http://www.microsoft.com/downloads/en/details.aspx?FamilyID=6b6c21d2-2006-4afa-9702-529fa782d63b&displaylang=en

2. 、Azure Management Tools CommandLetsを32bitのPowershellにインストールする方法

installutil.exe を使用して手動で.NETの32bit環境に手動でインストールを行う方法を紹介します。このinstallutil.exeは.NETでサービスを作った場合などを登録する際に使用するなど、一般的に利用するコマンドです。

2-1. コマンドプロンプトを管理者で実行
スタートメニューからコマンドプロンプトを管理者で実行します。管理者で実行する方法は、「コマンドプロンプト」のアイコンにマウスカーソルを合わせて右クリックで表示されるコンテキストメニューから「管理者として実行」を選択します。

2-2. installutil.exeを実行
下記コマンドのように、.NETの32bit版のインストールコマンドを実行します。指定するファイルは、Azure Management Tools CmdLets をインストールする際に作成されたDLLです。

%WINDIR%\Microsoft.NET\Framework\v2.0.50727\installutil.exe -i C:\AzureServiceManagementCmdlets\code\AzureManagementTools.Cmdlets\bin\Debug\Microsoft.Samples.AzureManagementTools.PowerShell.dll

2-3. インストール確認
前回ブログで紹介したプログラムで確認することもできますが、Windows PowerShell(x86)を実行してインストールの確認を行うことができます。

Windows PowerShell(x86)
%SystemRoot%\syswow64\WindowsPowerShell\v1.0\powershell.exe

C#のアプリケーションからPowerShell用Azure Service Management Tools CommandLets を使う

前回はAzure SDK 1.3 に PowerShellのAzure Service Management Tools CommandLets をインストールする方法を紹介しましたが、今回はインストールされたCommandLets を使用してC#のアプリケーションからAzureを操作してみます。

と、その前にPowerShellのAzure Service Management Tools CommandLetsのインストーラにはもう一つ罠があり、64bit環境で通常のインストールのままでは、今回紹介する方法は動作しません。64bit環境でも動作させる場合はAzure Service Management Tools CommandLetsのインストーラを少しいじる必要がありますので、その方法は次回紹介します。ですので、今回は32bit環境のWindowsで使用する方法の紹介となります。

PowerShellのAzure Service Management Tools CommandLets 単体の利用方法は、下記のサイトを確認していただければよいと思います。このサイトに紹介されている方法を使ってC#のアプリケーションから操作しますので、これらのサンプルコードは重要です。

http://www.microsoft.com/japan/powerpro/TF/column/st2_01_1.mspx
http://code.msdn.microsoft.com/azurecmdlets/Wiki/View.aspx?title=Sample%20Scripts&referringTitle=Home

では、さっそく方法を紹介します。

1.Visual Studioでプロジェクトを作成
.NET Frameworkを使用するプロジェクトを選択します。コンソールアプリケーションでもWPFでも構いません。

2. System.Management.Automation.dllを参照設定
ソリューションエクスプローラの参照から、System.Management.Automation.dllを参照します。インストールしてある場所は下記の場所です。(64bitな方は、次回紹介します。)

C:\Program Files\Reference Assemblies\Microsoft\WindowsPowerShell\v1.0\System.Management.Automation.dll

3.PowerShellを利用するコードを書く
参照したAutomationクラスを使用してPowerShellこのコマンドを実行します。ここで紹介するようにPowerShellクラスを作ると便利です。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Management.Automation;
using System.Management.Automation.Host;
using System.Management.Automation.Runspaces;
using System.Collections.ObjectModel;
using System.Threading;

namespace AzureCommandlets
{
    public class PowershellEventArgs : EventArgs
    {
        public Collection<psobject> CommandResults;
    }

    public delegate void PowershellEventHandler(object sender, PowershellEventArgs e);

    public class Powershell
    {
        public event PowershellEventHandler RunCommandCompleted;

        public void RunAsync(string command)
        {
            List<string> commands = new List<string>();
            commands.Add(command);
            ThreadPool.QueueUserWorkItem((unused) => RunComands(commands));
        }

        public void RunAsync(List<string> commands)
        {
            ThreadPool.QueueUserWorkItem((unused) => RunComands(commands));
        }

        public void RunComands(List<string> commands)
        {
            PowershellEventArgs e = new PowershellEventArgs();

            // Prepares azure management tool commandlets.
            RunspaceConfiguration rsConfig = RunspaceConfiguration.Create();
            PSSnapInException snapInException = null;
            PSSnapInInfo info = rsConfig.AddPSSnapIn("AzureManagementToolsSnapIn", out snapInException);
            Runspace myRunSpace = RunspaceFactory.CreateRunspace(rsConfig);
            myRunSpace.Open();

            // Runs commands.
            Pipeline pipeLine;
            pipeLine = myRunSpace.CreatePipeline();
            foreach (string command in commands)
            {
                pipeLine.Commands.Add(new Command(command, true));
            }
            e.CommandResults = pipeLine.Invoke();

            RunCommandCompleted(this, e);
            myRunSpace.Close();
        }
    }
}

インストールされた証明書を表示する場合は下記のように書きます。

static void Main(string[] args)
{
   Powershell ps = new Powershell();
   ps.RunCommandCompleted += new PowershellEventHandler(ps_RunCommandCompleted);
   ps.RunAsync("Get-Item cert:\\CurrentUser\\My\\*");
   Console.ReadLine();
}

static void ps_RunCommandCompleted(object sender, PowershellEventArgs e)
{
   var keys = from c in e.CommandResults
               where (from p in c.Properties
                      where "HasPrivateKey".Equals(p.Name) && true.Equals(p.Value)
                      select p).Any()
               select c;
    foreach (var key in keys)
    {
        Console.WriteLine(string.Format("Subject:{0} Thumbprint:{1}",
               key.Properties["Subject"].Value.ToString(),
               key.Properties["Thumbprint"].Value.ToString()));
    }
}

Hosted Serviceの情報を表示する場合は下記のように書きます。もし、パイプラインを使う場合は一行で書いてください。

static void Main(string[] args)
{
    Powershell ps = new Powershell();
    ps.RunCommandCompleted += new PowershellEventHandler(ps_RunCommandCompleted);
    List<string> commands = new List<string>();
    commands.Add(@"$cert=Get-Item cert:\CurrentUser\My\XXXXXXXXXXXXXXXXXXXXXXXX");
    commands.Add("$sub=\"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\"");
    commands.Add("$servicename=\"xxxxxxxxx\"");
    commands.Add("Get-HostedService $servicename -Certificate $cert -SubscriptionId $sub");
    ps.RunAsync(commands);
    Console.ReadLine();
}

static void ps_RunCommandCompleted(object sender, PowershellEventArgs e)
{
    foreach (var cmdlet in e.CommandResults)
    {
        foreach (var propInfo in cmdlet.Properties)
        {
            string value = string.Empty;
            try
            {
                if (propInfo != null && propInfo.Value != null)
                {
                    value = propInfo.Value.ToString();
                }
            }
            catch
            {
            }
            Console.WriteLine(propInfo.Name + ":" + value);
        }
    }
}

PowerShellのAzure Service Management Tools CommandLets を使わない方法として、C#のみでアプリケーションを完成させる場合、WebのAPIを直接触るか、Azure Service Management Tools CommandLetsやMMCに含まれている、Microsoft.Samples.WindowsAzure.ServiceManagementのプロジェクトを自分のアプリケーションから利用する方法がありますが、圧倒的にサンプルコード数が多いPowerShellのCommandLetsを使用した方が効率良いのではないでしょうか。

1/09/2011

Azure SDK 1.3 の環境で PowerShell の AzureManagementTools CmdLets をインストールする方法

Azure SDK 1.3 がリリースされた影響で、 PowerShellのAzure Management Tools Cmdlets がインストールできないなど色々と問題が出ています。
Azure SDK 1.2 の頃から利用している人は、Azure SDK 1.3 でアップグレードインストールした段階で、.NETのアセンブリリダイレクトを設定すればよいのですが、全く新しい環境に Azure Management Tools Cmdlets をインストールする場合は、インストール時にビルドエラーが発生してインストールに失敗してしまします。

Azure SDK 1.3 に対応させるアセンブリリダイレクトの設定について
http://sqlazure.jp/b/?p=368

ここでは、Azure SDK 1.3 しかインストールされていない状態で、Azure Management Tools Cmdlets をインストールする方法を紹介します。この方法で新しい環境に Azure Management Tools Cmdlets をインストールすることが可能になります。

Azure Management Tools Cmdletsの使用するインストールファイルは、以下の AzureServiceManagementCmdlets.Setup.20100226 になります。(Azure SDK 1.3 に対応しているものが出た場合は、この作業を行わなくてもよいでしょう)

Azure Management Tools Cmdlets
http://code.msdn.microsoft.com/azurecmdlets

では、インストールしてみましょう。

1.プロジェクトファイルの書き換え
ダウンロードした AzureServiceManagementCmdlets.Setup.20100226.exe を実行すると、 C:\AzureServiceManagementCmdlets フォルダにファイルが展開されます。このフォルダ内の startHere.cmd を実行してインストールを行うわけですが、実行する前に以下のファイルをメモ帳などで編集します。

C:\AzureServiceManagementCmdlets\code\AzureManagementTools.Cmdlets\AzureManagementTools.Cmdlets.csproj

編集場所はXMLで記述されている Projectエレメント内、ItemGroupエレメント内、Reference エレメントです。このReferenceエレメントのInclude アトリビュートに Microsoft.WindowsAzure.StorageClient が含まれる物を探してVersionを1.0.0.0から1.1.0.0に変更します。

以下の一行の変更するだけです。

<reference Include="Microsoft.WindowsAzure.StorageClient, Version=1.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL" />

変更後の AzureManagementTools.Cmdlets.csproj は以下のようになります。

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
    <ProductVersion>9.0.30729</ProductVersion>
    <SchemaVersion>2.0</SchemaVersion>
    <ProjectGuid>{CD5AA507-F5EF-473D-855B-84B91A1ABE54}</ProjectGuid>
    <OutputType>Library</OutputType>
    <AppDesignerFolder>Properties</AppDesignerFolder>
    <RootNamespace>Microsoft.Samples.AzureManagementTools.PowerShell</RootNamespace>
    <AssemblyName>Microsoft.Samples.AzureManagementTools.PowerShell</AssemblyName>
    <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
    <FileAlignment>512</FileAlignment>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
    <DebugSymbols>true</DebugSymbols>
    <DebugType>full</DebugType>
    <Optimize>false</Optimize>
    <OutputPath>bin\Debug\</OutputPath>
    <DefineConstants>DEBUG;TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
    <DebugType>pdbonly</DebugType>
    <Optimize>true</Optimize>
    <OutputPath>bin\Release\</OutputPath>
    <DefineConstants>TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x64' ">
    <DebugSymbols>true</DebugSymbols>
    <OutputPath>bin\x64\Debug\</OutputPath>
    <DefineConstants>DEBUG;TRACE</DefineConstants>
    <DebugType>full</DebugType>
    <PlatformTarget>x64</PlatformTarget>
    <CodeAnalysisUseTypeNameInSuppression>true</CodeAnalysisUseTypeNameInSuppression>
    <CodeAnalysisModuleSuppressionsFile>GlobalSuppressions.cs</CodeAnalysisModuleSuppressionsFile>
    <ErrorReport>prompt</ErrorReport>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x64' ">
    <OutputPath>bin\x64\Release\</OutputPath>
    <DefineConstants>TRACE</DefineConstants>
    <Optimize>true</Optimize>
    <DebugType>pdbonly</DebugType>
    <PlatformTarget>x64</PlatformTarget>
    <CodeAnalysisUseTypeNameInSuppression>true</CodeAnalysisUseTypeNameInSuppression>
    <CodeAnalysisModuleSuppressionsFile>GlobalSuppressions.cs</CodeAnalysisModuleSuppressionsFile>
    <ErrorReport>prompt</ErrorReport>
  </PropertyGroup>
  <ItemGroup>
    <Reference Include="Microsoft.WindowsAzure.Diagnostics, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL" />
    <Reference Include="Microsoft.WindowsAzure.StorageClient, Version=1.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL" />
    <Reference Include="System" />
    <Reference Include="System.Configuration.Install" />
    <Reference Include="System.Core">
      <RequiredTargetFramework>3.5</RequiredTargetFramework>
    </Reference>
    <Reference Include="System.Management.Automation, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
      <SpecificVersion>False</SpecificVersion>
      <HintPath>..\..\..\..\..\..\..\Program Files\Reference Assemblies\Microsoft\WindowsPowerShell\v1.0\System.Management.Automation.dll</HintPath>
    </Reference>
    <Reference Include="System.Runtime.Serialization">
      <RequiredTargetFramework>3.0</RequiredTargetFramework>
    </Reference>
    <Reference Include="System.ServiceModel">
      <RequiredTargetFramework>3.0</RequiredTargetFramework>
    </Reference>
    <Reference Include="System.ServiceModel.Web">
      <RequiredTargetFramework>3.5</RequiredTargetFramework>
    </Reference>
    <Reference Include="System.Xml.Linq">
      <RequiredTargetFramework>3.5</RequiredTargetFramework>
    </Reference>
    <Reference Include="System.Data.DataSetExtensions">
      <RequiredTargetFramework>3.5</RequiredTargetFramework>
    </Reference>
    <Reference Include="System.Data" />
    <Reference Include="System.Xml" />
  </ItemGroup>
  <ItemGroup>
    <Compile Include="AffinityGroups\GetAffinityGroup.cs" />
    <Compile Include="AffinityGroups\GetAffinityGroups.cs" />
    <Compile Include="AzureManagementToolsSnapIn.cs">
      <SubType>Component</SubType>
    </Compile>
    <Compile Include="Certificates\AddCertificate.cs" />
    <Compile Include="Certificates\RemoveCertificate.cs" />
    <Compile Include="Certificates\GetCertificate.cs" />
    <Compile Include="Certificates\GetCertificates.cs" />
    <Compile Include="CmdletBase.cs" />
    <Compile Include="ConfigurationConstants.cs" />
    <Compile Include="Diagnostics\GetActiveTransfers.cs" />
    <Compile Include="Diagnostics\GetCommonConfigurationLogs.cs" />
    <Compile Include="Diagnostics\SetCommonConfigurationLogs.cs" />
    <Compile Include="Diagnostics\StopActiveTransfer.cs" />
    <Compile Include="Diagnostics\GetDiagnosticConfiguration.cs" />
    <Compile Include="Diagnostics\SetPerformanceCounter.cs" />
    <Compile Include="Diagnostics\SetWindowsEventLog.cs" />
    <Compile Include="Diagnostics\SetInfrastructureLog.cs" />
    <Compile Include="Diagnostics\SetFileBasedLog.cs" />
    <Compile Include="Diagnostics\InstrumentationCmdletBase.cs" />
    <Compile Include="Diagnostics\SetWindowsAzureLog.cs" />
    <Compile Include="Diagnostics\GetDiagnosticAwareRoleInstances.cs" />
    <Compile Include="Diagnostics\DiagnosticsCmdletBase.cs" />
    <Compile Include="Diagnostics\GetDiagnosticAwareRoles.cs" />
    <Compile Include="Diagnostics\StartOnDemandTransfer.cs" />
    <Compile Include="Helpers\AzureBlob.cs" />
    <Compile Include="Helpers\CmdletExtensions.cs" />
    <Compile Include="HostedServices\GetHostedService.cs" />
    <Compile Include="Model\CertificateConfiguration.cs" />
    <Compile Include="Model\CertificateContext.cs" />
    <Compile Include="Model\DeploymentInfoContext.cs" />
    <Compile Include="GlobalSuppressions.cs" />
    <Compile Include="Helpers\Utility.cs" />
    <Compile Include="HostedServices\NewDeployment.cs" />
    <Compile Include="HostedServices\RemoveDeployment.cs" />
    <Compile Include="HostedServices\GetDeployment.cs" />
    <Compile Include="HostedServices\GetHostedProperties.cs" />
    <Compile Include="HostedServices\GetHostedServices.cs" />
    <Compile Include="Common\GetOperationStatus.cs" />
    <Compile Include="HostedServices\MoveDeployment.cs" />
    <Compile Include="HostedServices\SetDeploymentConfiguration.cs" />
    <Compile Include="HostedServices\SetDeploymentStatus.cs" />
    <Compile Include="HostedServices\SetDeployment.cs" />
    <Compile Include="HostedServices\SetWalkUpgradeDomain.cs" />
    <Compile Include="Model\ManagementOperationContext.cs" />
    <Compile Include="Model\HostedServiceContext.cs" />
    <Compile Include="Model\RoleConfiguration.cs" />
    <Compile Include="Model\RoleInstance.cs" />
    <Compile Include="StorageServices\GetStorageKeys.cs" />
    <Compile Include="StorageServices\GetStorageProperties.cs" />
    <Compile Include="StorageServices\GetStorageServices.cs" />
    <Compile Include="StorageServices\NewStorageKey.cs" />
    <Compile Include="Properties\AssemblyInfo.cs" />
  </ItemGroup>
  <ItemGroup>
    <None Include="Model\ClassDiagram.cd" />
  </ItemGroup>
  <ItemGroup>
    <Content Include="Microsoft.Samples.AzureManagementTools.PowerShell.dll-Help.xml">
      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
    </Content>
  </ItemGroup>
  <ItemGroup>
    <ProjectReference Include="..\Microsoft.Samples.WindowsAzure.ServiceManagement\Microsoft.Samples.WindowsAzure.ServiceManagement.csproj">
      <Project>{A3BB648E-9C6D-4251-B0E1-6E05024AB71E}</Project>
      <Name>Microsoft.Samples.WindowsAzure.ServiceManagement</Name>
    </ProjectReference>
  </ItemGroup>
  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
       Other similar extension points exist, see Microsoft.Common.targets.
  <Target Name="BeforeBuild">
  </Target>
  <Target Name="AfterBuild">
  </Target>
  -->
</Project>

2. startHere.cmdの実行
startHere.cmdを実行してインストールします。

3. AzureServiceManagementCmdletsの確認
PowerShellを実行して下記のコマンドを実行します。
PS C:\Users\kentaro> Add-PSSnapin AzureManagementToolsSnapIn
PS C:\Users\kentaro> Get-Command -PSSnapIn AzureManagementToolsSnapIn

下記のような結果が返ってくればAzureServiceManagementCmdletsがインストールされていることが確認できます。


次は、C#から、AzureServiceManagementCmdlets を操作する方法を紹介できればと思います。