「Windowsの機能の有効化または無効化」を行うには・・・

Windows7以降のOSで、グループポリシーやバッチファイル等から「Windowsの機能の有効化または無効化」を行うには、DISMコマンドを使用します。DISMコマンドはWindows自動インストールキット(Windows AIK)という、Windowsを多数のPCに導入するための支援ツールの一つです。Windowsのインストール支援ツールはOSのメジャーバージョンが変わる度に大きな変更が加えられるのが常ですが、DISMコマンド自体はWindows7以降はWindows 10でも後方互換性は維持されているようです。

現在有効化または無効化されている機能の一覧を得るには以下のコマンドを実行します。

dism  /online /Get-Features

特定の機能を有効化するには以下のコマンドを実行します。

dism /online /Enable-Feature /FeatureName:[機能名]

特定の機能を無効化するには以下のコマンドを実行します。

dism /online /Disable-Feature /FeatureName:[機能名]

以下のように機能名を指定すれば、特定機能について有効化されているか取得できますが、%ERRORLEVEL%の値としては取得出来ないようです。

dism  /online /Get-Features /FeatureName:[機能名]

また依存関係を自動では解決してくれません。機能Bを有効化するために、機能Aが必要となる場合には、以下のように依存関係を意識して順番に有効化する必要があります。

dism /online /Enable-Feature /FeatureName:[機能A]
dism /online /Enable-Feature /FeatureName:[機能B]

Traffic ServerでWORDPRESSを高速化する

最近地道にトラフィックが増えてきているので、そろそろWORDPRESSの高速化を検討してみる。世間ではNginxがはやっているようだが、ここは敢えてApatch Traffic Serverを使ってみたい。

OSはUbuntu 15.xを使用しています。

Apatche Traffic Serverの初期設定

apt-getを使用して標準のレポジトリからApatche Traffic Serverをインストールします。

sudo apt-get install trafficserver

Reverse-Proxeの接続先を設定します。

sudo nano remap.config
map http://www.example.net:8080/ http://127.0.0.1/
reverse_map http://127.0.0.1/ http://www.example.net:8080/

サービスを起動してポート8080に接続してみますが、動作しません。

sudo service trafficserver start

ログファイルを閲覧すると、以下のようなエラーが発生しています。

less /var/log/trafficserver/traffic.out
traffic_server: using root directory '/usr'
FATAL: Trafficserver has not been designed to serve pages while
        running as root. There are known race conditions that
        will allow any local user to read any file on the system.
        If you still desire to serve pages as root then
        add -DBIG_SECURITY_HOLE to the CFLAGS env variable
        and then rebuild the server.
        It is strongly suggested that you instead modify the
        proxy.config.admin.user_id directive in your
        records.config file to list a non-root user.

どうやら標準のままだとrootアカウントで起動されてしまうけど、trafficserverはrootアカウントで起動できないため権限周りでエラーとなっているようです。/etc/trafficserver/records.configを編集してproxy.config.admin.user_idの設定を追加するように指示されているので追加します。trafficserverアカウントは既に作られているので、追加の必要は無いようです。

sudo nano /etc/trafficserver/records.config
CONFIG proxy.config.admin.user_id STRING trafficserver

サービスを再起動してポート8080に接続してみると、今度は動作しました。

sudo service trafficserver restart

サービスの移行

正常に動作するようなので、本番に移行します。
Windows Azureを使っているので、エンドポイントの設定を変更してポート80への接続を、ポート8080に振り分けるようにします。こういう所、手抜きできるからクラウド好きです。

remap.configの設定を本番用に変更します。先ほどはポート8080の記載がありましたが、それを削除します。

sudo nano remap.config
map http://www.example.net/ http://127.0.0.1/
reverse_map http://127.0.0.1/ http://www.example.net/

このままだとWORDPRESSが正常に動作しないので、以下の設定をwp-config.phpに追記します。

sudo nano /var/www/wordpress/wp-config.php
$_SERVER['HTTP_HOST'] = $_SERVER['HTTP_X_FORWARDED_HOST'];
$_SERVER['REMOTE_ADDR'] = $_SERVER['HTTP_X_FORWARDED_FOR'];

apatch2とtrafficserverを再起動します。

sudo nano /var/www/wordpress/wp-config.php
$_SERVER['HTTP_HOST'] = $_SERVER['HTTP_X_FORWARDED_HOST'];
$_SERVER['REMOTE_ADDR'] = $_SERVER['HTTP_X_FORWARDED_FOR'];

ポート80に繋いでみますが、正常に動作しているようです。

ローカルストレージへのキャッシュ移動

標準設定のままだと/var/cash/trafficserverにキャッシュデータを保存します。クラウド上の仮想サーバーは一般的にIO速度が遅いため、あまり速度が改善しません。その代わりにWindows Azureでは高速な揮発性のローカルストレージも提供されています。揮発性のストレージは仮想サーバをシャットダウンすると失われますが、キャッシュデータなら失われても問題ないのでこちらに移動します。
ローカルストレージはスワップファイル用に/mntにマウントされているので、/mnt/trafficserverと言うフォルダを新たに用意してこちらを使います。

新たにフォルダを作成します
sudo mkdir /mnt/trafficserver
sudo chown trafficserver /mnt/trafficserver

storage.configを編集して、キャッシュの保存先を変更します。
sudo nano /etc/trafficserver/storage.config
/mnt/trafficserver 256M

trafficserverを再起動します。
sudo service trafficserver restart

どの程度改善されたか

トップページの表示完了まで5,000msかかっていたのが700ms程度に。トップページのhtmlだけなら2,500ms程度かかっていたのが、20ms程度にまで高速化されました。応答速度が125倍改善して、体感的にも7倍くらい速くなっている計算。

参考

WordPressをリバースプロクシ対応にする3つのポイント
How to Set Up Apache Traffic Server as a Reverse-Proxy on Ubuntu 14.04

PowershellでActiveDirectoryのユーザーを管理する

ActiveDirectory(以下AD)のユーザーは人事異動等にともなって定期的に大きく変更されるので、手作用で更新していると非常に煩雑な事になります。Powershellの活用でADのユーザー管理の省力化を目指したいと思います。

Remote Server Administration Toolsのインストール

PowershellでActive Directoryのユーザー管理の為のコマンドレットを呼び出すためには、Remote Server Administration Tools(以下RSAT)をインストールする必要がある。インストールするRSATはクライアントOS毎に異なるので、対応するインストーラを以下からダウンロードする。
Remote Server Administration Tools (RSAT) for Windows Client and Windows Server (dsforum2wiki)

ADユーザーを更新する

更新する対象がデータベースなら削除して登録することを繰り返しても問題ない場合が多いが、ADユーザーの場合には削除して追加すると言うわけにはいきません。一度削除してしまうと、再登録してもユーザーを一意に識別するために使っているSIDやGUIDが異なる値になってしまい、別のユーザーとして識別されてしまいます。従って面倒でも既存のユーザーの有無を調べ、無ければ新規に作成し、あるなら必要に応じて項目を更新すると言う手続きにならざる得ません。

既存ユーザー有無確認

既存ユーザーの有無を確認するにはGet-ADUserを使う。Get-ADUserは一致するユーザーが存在しない場合には例外を投げるので、$existUserの値は変更されないまま$NULLとなる。

     $existUser = $NULL
     $existUser = Get-ADUser -Identity [ユーザー名]
     if ($existUser -eq $NULL)
     {
          # ユーザーが登録されていない場合の処理
     }
     else
     {
          # ユーザーが登録されている場合の処理
     }

ユーザーの追加

ユーザーを追加するにはNew-ADUserを使用します。ユーザーに関する諸設定を行うためにコマンドラインパラメータが非常に多いです。必要最低限を指定して、残りはSet-ADUserで処理した方が実装が容易かと思います。

     New-ADUser "[ユーザー名]"  -UserPrincipalName "[ユーザー名]@[ドメイン] .local") -DisplayName "[表示名]" -Path"OU=[新部署] , OU=[部門], DC=[ドメイン], DC=local " -Enabled $TRUE

既存ユーザーの一覧を取得

Get-ADUserは指定した条件にマッチするユーザーのリストを返す。フィルタ条件にアスタリスクを指定すると全てのユーザーが得られる。抽出条件の指定方法は多岐に渡るので詳細はオンラインヘルプに委ねる。

    $userAccountList = Get-ADUser -Filter *
     foreach ($userAccount in $userAccountList)
     {
          # 各ユーザーに対する処理
     }

組織単位の移動

組織単位はGet-ADUser で取得したユーザーアカウントのプロパティを参照すれば得られる。組織単位を変更する場合には、Move-ADObjectを使用して移動先の組織単位を設定する。

     if ($userAccount.DistinguishedName -notmatch ".*OU=[旧部署].*")
     {
           Move-ADObject -Identity $userAccount. ObjectGUID -TargetPath "OU=[新部署] , OU=[部門], DC=[ドメイン], DC=local"
     }

グループ設定の変更

ユーザーグループに割り当てるにはaddNoneGroupMemberを、ユーザーグループから削除するにはremoveExistGroupMemberを使う。

     # グループに追加する
     addNoneGroupMember $userAccount "グループ名"

     # グループから削除する
     removeExistGroupMember $userAccount "グループ名"

ユーザーの削除

Remove-ADUserを使えば削除する事が出来るが、誤って削除してしまうと同じSIDやGUIDを持つユーザーを復活させようとすると非常に難しくなる。従って削除するのではなくSet-ADUserでユーザーを無効にした上で、特定の組織単位に移動するに止める方が良い。

     if ($userAccount . Enabled)
     {
          $userAccount .Enabled = $FALSE
          Set-ADUser -Instance $userAccount
     }

     if ($userAccount . DistinguishedName -notmatch ".*OU=削除対象.*" )
     {
           Move-ADObject -Identity $userAccount. ObjectGUID -TargetPath "OU=削除対象 , OU=[部門], DC=[ドメイン], DC=local"
     }

グループポリシーでODBCデータソースを設定する

グループポリシーエディタの基本設定→コントロールパネルの設定→データソースの設定を開き、新たにデータソースの設定を追加します。
odbc_property
データソースのプロパティで操作に置換を選択します。
置換を選択すると既存の設定がない場合には新たに作成、既存の設定がある場合にはそれを削除して新たに作成します。更新の場合には既存の設定がある場合には、これを上書きします。作成の場合には既存の設定がない場合には新たに作成、既存の設定がある場合には何もしません。
続いてデータソース名を入力し、ドライバを選択します。 データソースのプロパティにはユーザー名、パスワードといった項目がありますが指定することはできません。
このままだと接続先サーバー名はデータソース名と同じになります。ほとんどの場合データソース名の、接続先サーバーのコンピュータ名は異なるでしょうから、サーバー名を指定する必要があります。
接続先サーバー名、データベース名は属性として指定します。
odbc_atrubutes
例えば接続先サーバー名を指定するには、属性の追加ボタンをクリックして、表示されるダイアログの名前にServer、値に接続先サーバーのコンピューター名を指定します。同様にデータベース名は名前にDatabase、値にデータベース名を指定します。
最後にログインしたユーザーをここで指定することはできません。属性の名前にLastUserを指定すると動作しなくなります。指定したい場合にはデータソースの設定ではなく、レジストリの設定で行うとよいです。
そのほかの属性名を知りたい場合には、コントロールパネルのODBCから設定を行った後、レジストリ(HKEY_LOCAL_MACHINE\SOFTWARE\ODBC\ODBC.INI)の設定を確認してください。ここにデータソース名ごとに実際に設定されている属性が保存されています。実際にはLastUserのように属性として指定できない場合もあるので、レジストリの設定で行ったほうが面倒がないかもしれません。

フォルダを開こうとすると「サーバーの実行に失敗しました。」と表示される

Windows 7でexplorer.exeを起動し、フォルダやコンピュータのプロパティ、コントロールパネル等を開こうとすると、しばらく待った後「 サーバーの実行に失敗しました。」とエラーが表示され、開くことができなくなった。

原因は標準のユーザーフォルダが失われた為でした。 レジストリの”HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders”にある、”Personal”の値が%USERPROFILE%\Desktop\Documentsに書き換わっていました。その後何かの拍子にDesktop\Documentsを削除したためマイドキュメントフォルダがなくなり、その為にExplorer.exeが動作しなくなっていました。

まずはレジストリエディタを起動して、”HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders”にあるPersonalの内容をデフォルトの%USERPROFILE%\Documentsに戻します。次にコマンドプロンプトを開いてmkdir Documentsを実行し、あるべき場所にマイドキュメント用のフォルダを作成しました。

これでフォルダが開けるようになり解決しました。

参考にしたブログ:http://meochika-value.hatenablog.com/entry/2013/07/27/142113

Google Blogger API v3.0を使ってエントリを書き込む

先日、C#からGoogle Blogger API v3.0を使用した。
検索してもサンプルが見当たらなかったので、ブログを書き込むサンプルをメモとして公開しておく。

準備

Google Blogger API v3.0はNuGetからインストールします。

コード

using Google.Apis.Auth.OAuth2;
using Google.Apis.Services;
using Google.Apis.Util.Store;
using Google.Apis.Blogger;
using Google.Apis.Blogger.v3;
using Google.Apis.Blogger.v3.Data;
//...
    static void Main(string[] args)
    {
       // OAuth 認証を行う
        UserCredential credential;
        using (var stream = new FileStream("client_id.json", FileMode.Open, FileAccess.Read))
        {
            credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(
                    GoogleClientSecrets.Load(stream).Secrets,
                    new[] { BloggerService.Scope.Blogger },
                    "user", CancellationToken.None);
        }

       // Bloggerのインスタンスを取得
        BloggerService service = new BloggerService(new BaseClientService.Initializer()
        { HttpClientInitializer = credential, ApplicationName = "Blogger Convert" });

       // Blogの一覧を取得
       var blogList = service.Blogs.ListByUser("self").Execute();

      // Blogに新しいエントリを作成する
       var newPost = new Post();
       newPost.Title = "blog title";
       newPost.Content = "blog body text
"; newPost.Published = DateTime.Prase("2016-01-01 12:00"); var updPost = service.Posts.Insert(newPost, blogList.Items[0].Id).Execute(); //...

ちなみにバグがあってブログのラベルを設定することは出来ない。もう随分長いこと放置されているので、修正の見込はないと思っておこう。
画像データをアップロードする機能は見当たらない。おそらくはPicasaと連携しろって事なのだとも思うけど・・・Picasaって2016年5月にサービス終了するんだよな。

C#でSIMDを使って高速化

そういえばVisual Studio 2015からC#でもSIMDが使えるようになったんだっけ・・・と言うことで使ってみた。
SIMDを有効にするには3つの条件がある。

  • .NET Framework 4.6を使用すること。
  • 64bitコードを選択すること。
  • NuGetからNumerics.Vectors(v4.1)をインストールすること。

プロジェクトのプロパティを開いて次の二ヶ所のオプションを変更しておく。
option#2
option#1
次にNuGetパッケージの管理を開いて、System.Numerics.Vectorsを追加する。
nuget

これで準備は出来たのでコードを書いてみる。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Numerics;

namespace SimdTest
{
    class Program
    {
        static void Main(string[] args)
        {
            DateTime startTime = DateTime.Now;
            double answer = 0.0;
            for (int i = 0;i < 1000000;i++)
            {
                answer = 1.0;
                for (int j = 1;j <= 100;j++)
                {
                    answer = answer * (double )j;
                }
            }
            DateTime lastTime = DateTime.Now;
            Console.WriteLine("{0}ms, answer = {1}", (lastTime - startTime).TotalMilliseconds, answer);

            Console.WriteLine("Count = {0}, Hardware = {1}", Vector<double>.Count, Vector.IsHardwareAccelerated);
            startTime = DateTime.Now;
            for (int i = 0; i < 1000000; i++)
            {
                Vector<double> answerTemp = new Vector<double>(new double[] {1, 1, 1, 1});
                Vector<double> multiTemp = new Vector<double>(new double[] {0, 25, 50, 75});
                for (int j = 1; j <= 25; j++)
                {
                    multiTemp += Vector<double>.One;
                    answerTemp *= multiTemp;
                }
                answer = answerTemp[0] * answerTemp[1] * answerTemp[2] * answerTemp[3];
            }
            lastTime = DateTime.Now;
            Console.WriteLine("{0}ms, answer = {1}", (lastTime - startTime).TotalMilliseconds, answer);
        }
    }
}

と言うわけで、100の階乗を求める処理を書いてみた。
非常にシンプルな例なのだが、35%程度の高速化にしかならない。
Vector.IsHardwareAcceleratedを参照すると確かにSIMDが使われている事が分かる。
Vector<double>.Countが4を返すのでSSEではなくIntel AVXにも対応している様子。AVX非対応(SSE2対応)PCだとVector<double>.Countが2を返してきます。
本来的にはVector<double>.Countの値を見て並列数を判断するべきなのかもしれないが、4で決め打ちしています。

#OpenCL使えば手っ取り早いダロという話もあるけど、自宅や会社で使ってるPCが対応していないんだよね。

C#でExcelに画像データを埋め込む

※追記…ClosedXML v0.88移行は標準で画像の埋め込みに対応しており、ClosedXMLImageSupportを使う必要がなくなりました。

OpenXML SDKで画像を埋め込んだExcelファイルを作成しようとすると、ステップ数が一気に増えてしまって現実的ではありません。ClosedXMLはExcelファイルを作るのにとっても便利なのですが、画像データを扱う機能がありません・・・・と思ったら、ClosedXMLをForkして画像データを扱う機能を追加している方が、ここは感謝して使わせていただきましょう。

ClosedXML.DLLをビルドする

NuGetでダウンロードできるCloseXMLは画像データを扱えませんから、自分でソースコードからビルドします。Fork: ajwhiteway/ClosedXMLImageSupportからソースコードをダウンロードします。
ClosedXML_for_image_download
ダウンロードしたzipファイルを解凍するとソースコードが得られるので、ClosedXML.slnファイルを開いてビルドします。ClosedXML\bin\Releaseに生成されるClosedXML.dllを参照設定します。

これで準備は出来ました。

using BarcodeLib;
using ClosedXML.Excel;
using ClosedXML.Excel.Drawings;
<<中略>>
            var book = new XLWorkbook();
            var sheet = book.AddWorksheet("sheet1");

            // 画像データを指定
            XLPicture pic = new XLPicture
            {
                NoChangeAspect = true,
                NoMove = true,
                NoResize = true,
                ImageStream = File.OpenRead("sample.png")
            };

            // 表示位置を指定
            XLMarker fMark = new XLMarker
            {
                ColumnId = 1,
                RowId = 1
            };
            pic.AddMarker(fMark);

            // 画像データを追加
            sheet.AddPicture(pic);
            book.SaveAs("sample.xlsx");
<<中略>>

と、かなりシンプルに記述できます。
こんなに便利なのだから是非mainに取り込んで欲しいよね。

Windows Azure上のWindowsをGUIなしにする

多少でも仮想マシンの利用料を下げるために、GUIを削除して軽くしたいと思います。これで仮想マシンのサイズを下げても、使用メモリ量が減っている分、それなりに使えるようになるはずです。

仮想OSのPowershellのリモート接続を有効にする

GUIを持たないWindowsの操作は主にPowerShellから行う事になります。まずはPowerSheelに対してリモート接続出来る状態にします。Azure仮想OSにRemote Desktopで接続して、PowerShellを管理者権限で起動してEnable-PSRemoteingを実行します。これにより必要なサービスの起動やファイアウォールの設定等が自動で行われますので「[A] Yes to All」を選択して継続してください。

PS C:\Users\username&gt; Enable-PSRemoting
WinRM Quick Configuration
Running command "Set-WSManQuickConfig" to enable remote management of this computer by using the Windows Remote
Management (WinRM) service.
 This includes:
    1. Starting or restarting (if already started) the WinRM service
    2. Setting the WinRM service startup type to Automatic
    3. Creating a listener to accept requests on any IP address
    4. Enabling Windows Firewall inbound rule exceptions for WS-Management traffic (for http only).

Do you want to continue?
[Y] Yes  [A] Yes to All  [N] No  [L] No to All  [S] Suspend  [?] Help (default is "Y"):

クライアントへのSSL証明書のインストール

次にPowerShellのリモート接続で使用するためのSSL証明書のダウンロードとインストールを行います。少々変則的ですがhttps://[仮想マシン名].cloudapp.net:5986のURLにGoogle Chromeを使用してアクセスし、画面赤丸の部分をクリックして証明書を表示します。
chrome#1

次に↓画面の赤丸の部分をクリック。
chrome#2

最後に↓画面の赤丸部分をクリックして、後はウィザードに従ってデフォルト設定のままファイルに保存します。
chrome#3

証明書をダウンロードしたら、証明書ファイルを右クリックしてインストールを選択します。
inport#1

インストールはウィザードに従って勧めていきますが、証明書ストアの選択で「信頼されたルート証明機関」に保存するように設定をしてください。それ以外はデフォルトのままで進めます。
inport#3

クライアントから仮想OSにPowerShellで接続する

これでPowerShellでリモート接続する準備が整ったので、PowerShellを起動して次のコマンドを実行します。プロンプトに接続先コンピュータ名が表示され、リモート接続していることが分かります。

PS C:\Users\username> Enter-PSSession -ComputerName computername.cloudapp.net -Credential knarita -UseSSL
[computername.cloudapp.net]: PS C:\Users\username\Documents>

 

仮想OSからGUIを削除する

せっかくなので、このままAzure仮想OSからGUIをアイインストールしてしまいます。次のコマンドを実行してください。

Uninstall-WindowsFeature Server-Gui-Mgmt-Infra –restart
Success Restart Needed Exit Code      Feature Result
------- -------------- ---------      --------------
True    Yes            SuccessRest... {Windows PowerShell ISE, Graphical Managem...
警告: You must restart this server to finish the removal process.

これで再起動するとGUIが無い状態で起動します。

ちなみにGUIを戻すにはPowerShellから次のコマンドを実行すると、いつでも元に戻せます。

Install-WindowsFeature Server-Gui-Mgmt-Infra,Server-Gui-Shell –restart

apt-get upgradeでThe following packages have been kept backが発生

Ubuntu 15.10にてapt-get upgradeを実行したときに、下記のようにThe following packages have been kept backと表示されアップデートが行われない。既存パッケージの依存関係に問題が発生しているのが原因。

$ sudo apt-get upgrade
Reading package lists... Done
Building dependency tree
Reading state information... Done
Calculating upgrade... Done
The following packages have been kept back:
  linux-cloud-tools-virtual linux-generic linux-headers-generic
  linux-headers-virtual linux-image-extra-virtual linux-image-generic
  linux-image-virtual linux-virtual sosreport
0 upgraded, 0 newly installed, 0 to remove and 9 not upgraded.

sudo apt-get dist-upgradeを実行すると依存関係の問題が処理される。longer required: linux-cloud-tools-4.2.0-16と表示されているようにlinux-cloud-toolsのupdateを自動で処理できなかったのが原因。このままYとするとlinux-cloud-tools-4.2.0-22に更新されてインストールが完了する。

$ sudo apt-get dist-upgrade
Reading package lists... Done
Building dependency tree
Reading state information... Done
Calculating upgrade... The following packages were automatically installed and are no longer required:
  linux-cloud-tools-4.2.0-16 linux-cloud-tools-4.2.0-16-generic
Use 'apt-get autoremove' to remove them.
Done
The following NEW packages will be installed:
  libpython3.5-minimal libpython3.5-stdlib linux-cloud-tools-4.2.0-22
  linux-cloud-tools-4.2.0-22-generic linux-headers-4.2.0-22
  linux-headers-4.2.0-22-generic linux-image-4.2.0-22-generic
  linux-image-extra-4.2.0-22-generic python3.5 python3.5-minimal
The following packages will be upgraded:
  linux-cloud-tools-virtual linux-generic linux-headers-generic
  linux-headers-virtual linux-image-extra-virtual linux-image-generic
  linux-image-virtual linux-virtual sosreport
9 upgraded, 10 newly installed, 0 to remove and 0 not upgraded.
Need to get 70.8 MB of archives.
After this operation, 311 MB of additional disk space will be used.
Do you want to continue? [Y/n]

linux-cloud-toolsは不用意に更新すると接続が出来なくなったり、クラウドの管理コンソールから制御できなくなる恐れがあるので、自動更新を取り止めたのかと思われる。