Powershellでローカルユーザーを管理する(Microsoft.PowerShell.LocalAccounts)

昨日の「Hey, Scripting Guy」!*1を読んでいて知ったのだが、最新のPowershell*2ではローカルユーザーを管理できる。

実行環境

Microsoft Windows 10 Pro (10.0.14393)

Major  Minor  Build  Revision
-----  -----  -----  --------
5      1      14393  576

実際のコマンド

まずはどんなコマンドがあるか、手元の環境で確認してみる。

PS D:\SandBox> # とりあえずコマンドを引く

PS D:\SandBox> Get-Command Get-LocalUser

CommandType     Name                                               Version    Source                                                                                           
-----------     ----                                               -------    ------                                                                                           
Cmdlet          Get-LocalUser                                      1.0.0.0    Microsoft.PowerShell.LocalAccounts                                                               



PS D:\SandBox> # 同じSourceに似たようなコマンドがないか調べる

PS D:\SandBox> get-command -Module  Microsoft.PowerShell.LocalAccounts

CommandType     Name                                               Version    Source                                                                                           
-----------     ----                                               -------    ------                                                                                           
Cmdlet          Add-LocalGroupMember                               1.0.0.0    Microsoft.PowerShell.LocalAccounts                                                               
Cmdlet          Disable-LocalUser                                  1.0.0.0    Microsoft.PowerShell.LocalAccounts                                                               
Cmdlet          Enable-LocalUser                                   1.0.0.0    Microsoft.PowerShell.LocalAccounts                                                               
Cmdlet          Get-LocalGroup                                     1.0.0.0    Microsoft.PowerShell.LocalAccounts                                                               
Cmdlet          Get-LocalGroupMember                               1.0.0.0    Microsoft.PowerShell.LocalAccounts                                                               
Cmdlet          Get-LocalUser                                      1.0.0.0    Microsoft.PowerShell.LocalAccounts                                                               
Cmdlet          New-LocalGroup                                     1.0.0.0    Microsoft.PowerShell.LocalAccounts                                                               
Cmdlet          New-LocalUser                                      1.0.0.0    Microsoft.PowerShell.LocalAccounts                                                               
Cmdlet          Remove-LocalGroup                                  1.0.0.0    Microsoft.PowerShell.LocalAccounts                                                               
Cmdlet          Remove-LocalGroupMember                            1.0.0.0    Microsoft.PowerShell.LocalAccounts                                                               
Cmdlet          Remove-LocalUser                                   1.0.0.0    Microsoft.PowerShell.LocalAccounts                                                               
Cmdlet          Rename-LocalGroup                                  1.0.0.0    Microsoft.PowerShell.LocalAccounts                                                               
Cmdlet          Rename-LocalUser                                   1.0.0.0    Microsoft.PowerShell.LocalAccounts                                                               
Cmdlet          Set-LocalGroup                                     1.0.0.0    Microsoft.PowerShell.LocalAccounts                                                               
Cmdlet          Set-LocalUser                                      1.0.0.0    Microsoft.PowerShell.LocalAccounts                                                                

続けていくつか使ってみる。

PS D:\SandBox>  #ローカルユーザーの一覧から最初3つを取得
PS D:\SandBox> (Get-LocalUser)[1..3]

Name           Enabled Description
----           ------- -----------
DefaultAccount False   システムで管理されるユーザー アカウントです。
Guest          False   コンピューター/ドメインへのゲスト アクセス用 (ビルトイン アカウント)
HomeGroupUser$ True    コンピューターへのホームグループ アクセス用のビルトイン アカウント


PS D:\SandBox>  #ローカルグループの一覧から最初3つを取得
PS D:\SandBox> (Get-LocalGroup)[1..3]

Name                                Description
----                                -----------
WinRMRemoteWMIUsers__               Members of this group can access WMI resources over management protocols (such a...
__vmware__                          VMware User Group
Access Control Assistance Operators このグループのメンバーは、このコンピューター上のリソースの認証属性およびアクセス...

PS D:\SandBox>  #Administratorsグループのメンバーを取得
PS D:\SandBox> Get-LocalGroupMember Administrators

ObjectClass Name                   PrincipalSource
----------- ----                   ---------------
ユーザー    examplePC\Administrator Local

もっと詳しい使い方は公式のリファレンスを読めばよい。 Microsoft.PowerShell.LocalAccounts

参考

PowerTip: Get a list of local Users in Windows 10 / Windows Server 2016 – Hey, Scripting Guy! Blog ⇒元ネタ

Tech TIPS:Windowsのnet userコマンドでユーザーアカウントを管理する - @IT ⇒該当コマンドレットが利用できない場合、昔ながらのnet user コマンドを使う。

余談

bashならどうするの?というメモ

ユーザーの情報を取得したいだけならgetentコマンドを利用する。/etc/groupだけではわからないようなLDAP連携の値も持ってこれる。

変更を加えたいなら

  • user(add|del|mod)
  • group(add|del|mod)

Powershellでループを書かずに指定回数処理を繰り返す

範囲演算子(..)とパイプ(|)でループ処理のようなことができる

普通にループで書くなら以下のようになる。

#forループの場合
for ($i=0; $i -lt 10; $i++){ echo "hoge" }
#foreachループの場合
foreach($cnt in 1..10){ echo "hoge" }

でも下のほうがすっきりしていると思う

PS D:\SandBox> 1..10 | %{ echo "hoge"}

変数も使える(一応実行例も載せた)

PS D:\SandBox> $limit=10
PS D:\SandBox> 1..$limit | %{ echo "hoge"}
hoge
hoge
hoge
hoge
hoge
hoge
hoge
hoge
hoge
hoge

何が起こっているのか

  • 範囲演算子で指定個数の整数が生成される
  • パイプで渡してforeach-object(=%)する際は、普通は渡したオブジェクト($_)に対して処理をする
  • が、あえてそれをしないことで渡したオブジェクトの数だけ処理を実行させる

参考

05.範囲演算子 < 演算子 Tips メニュー < PowerShell Tips < HIRO's.NET ⇒範囲演算子の簡単な説明

Powershellでプリンターの状態を取得する

Powershellでプリンタの状態(ステータス)を取得するにはPrintManagementモジュールのGet-Printerを利用すればよい。

必要な環境

Powershll 3.0 以降

実行例

Get-Printerコマンドレットで特定のプリンタ名を指定してでCIMインスタンス*1を取得し、PrinterStatusを取得する*2。Normalなら利用できる状態のようだ。

PS D:\SandBox> (Get-Printer –Name "Microsoft XPS Document Writer").PrinterStatus
Normal

※実行環境は以下

PS D:\SandBox> "$((get-wmiobject win32_operatingsystem).caption) ($((get-wmiobject win32_operatingsystem).version))"
Microsoft Windows 10 Pro (10.0.14393)

PS D:\SandBox> $PSVersionTable.PSVersion

Major  Minor  Build  Revision
-----  -----  -----  --------
5      1      14393  206     

参考

PrintManagementモジュールについて
CIMについて
  • Windows用語集 - WMI:ITpro CIM(Common Information Model)はWIM(Windows Management Instrumentation)の一部で監視情報のデータ構造を標準化するための形式、と理解した。
Powershellをバージョン指定で実行する方法について

余談

いつもの通り、英語版も含めてPowershellのレファレンスはクソなので何とかしてほしい。 PrinterStatusが取りうる値の範囲ぐらい明記してほしい。

*1:正確には「Microsoft.Management.Infrastructure.CimInstance#ROOT/StandardCimv2/MSFT_Printer」

*2:取得したインスタンスのメンバーを確認すると「ScriptProperty System.Object PrinterStatus」とあるので、PrintManagementのみの独自拡張なのだと思う

Powershellでアナグラムの判定をする

技術面接で出された問題 - esm アジャイル事業部 開発者ブログ

上記のエントリを読んでPowershellだとどうやって実装するかなーと思ったので、テキトーに試す。

ソートだとこんな感じ?*1

[String]$a="ABCB"
[String]$b="BCAB"

if($a -eq $b){return $false} # 完全に一致する場合はアナグラムではないのでNG

#$aと$bをソートした上で比較する。一致すればアナグラム。
$aa=""
$bb=""
if(($a.ToCharArray() | Sort-Object | %{$aa=$aa + $_}) -eq ($b.ToCharArray() | Sort-Object | %{$bb=$bb + $_})){return $true}else{return $false}

AとBに含まれる文字ごとの個数が一致すればOKのはずなので、もうちょっと真面目に書くとこんな感じ?

[String]$a="ABCB"
[String]$b="BCAB"

if($a -eq $b){return $false} #完全に一致する場合はアナグラムではないのでNG

$hash1=@{}

$a.ToCharArray() | %{if($hash1.ContainsKey($_)){$hash1[$_]=$hash1[$_]+1}else{$hash1[$_]=1}};
$b.ToCharArray() | %{if($hash1.ContainsKey($_)){$hash1[$_]=$hash1[$_]-1}else{$hash1[$_]=-1}};

$flg=$true

$hash1.Values | %{if($_ -ne 0){$flg=$false}} 

return $flg

関数として使えるものにするなら、以下のようなこともしなくてはならないけど、まあこんな感じで実装できそうだということの確認にはなった。

  • 引数で文字列を受けとれるようにする
  • 引数チェックする($nullチェックとか?)
  • 性能評価*2

*1:ソートはもう少しましなやり方があるような気もするのだけれど、”配列の比較”の実装に悩んでしまったので、ソートした後に文字列として連結して比較している。単純に-eqなどで比較すると同一オブジェクトかどうかを比較してしまうので

*2:ちょっと真面目に書いた方はおそらくO(n)にはなっていると思うけど。

Copy-itemを使ったディレクトリコピーはしないほうが無難なのでは、という話

Copy-itemを使ったディレクトリコピーはコピー先に指定するディレクトリの有無に依存して挙動が変わってしまう。

面倒でもディレクトリコピーはしないほうが無難であり、コピー先フォルダを作成した上で、コピー元フォルダ以下をワイルドカードコピーすべきでは、という話

というか、ディレクトリコピーをするならPowershellにこだわらずrobocopyを使ったほうが良いのだろうと思う*1

環境

PS D:\SandBox> "$((get-wmiobject win32_operatingsystem).caption) ($((get-wmiobject win32_operatingsystem).version))"
Microsoft Windows 10 Pro (10.0.10586)

PS D:\SandBox> $PSVersionTable.PSVersion

Major  Minor  Build  Revision
-----  -----  -----  --------
5      0      10586  494 

実際に試してみる

PS D:\SandBox> #フォルダ構成

PS D:\SandBox> tree /f D:\SandBox
フォルダー パスの一覧
ボリューム シリアル番号は 4641-823A です
D:\SANDBOX
└─from
        test1.txt
        test2.txt
        

PS D:\SandBox> #コピー先ディレクトリが存在しない場合にディレクトリコピーする

PS D:\SandBox> Copy-Item .\from .\to -Recurse -Force -PassThru


    ディレクトリ: D:\SandBox


Mode                LastWriteTime         Length Name                                                                                                                          
----                -------------         ------ ----                                                                                                                          
d-----       2016/09/19     15:35                to                                                                                                                            


    ディレクトリ: D:\SandBox\to


Mode                LastWriteTime         Length Name                                                                                                                          
----                -------------         ------ ----                                                                                                                          
-a----       2016/09/19     15:31             14 test1.txt                                                                                                                     
-a----       2016/09/19     15:31             14 test2.txt                                                                                                                     



PS D:\SandBox> #コピー先フォルダが作成され、ディレクトリがコピーされる

PS D:\SandBox> tree /f D:\SandBox
フォルダー パスの一覧
ボリューム シリアル番号は 4641-823A です
D:\SANDBOX
├─from
│      test1.txt
│      test2.txt
│      
└─to
        test1.txt
        test2.txt
        

PS D:\SandBox> 
PS D:\SandBox> #上記のようにコピー先フォルダが存在する状態で、先ほどと同じコマンドでディレクトリコピーする

PS D:\SandBox> Copy-Item .\from .\to -Recurse -Force -PassThru


    ディレクトリ: D:\SandBox\to


Mode                LastWriteTime         Length Name                                                                                                                          
----                -------------         ------ ----                                                                                                                          
d-----       2016/09/19     15:37                from                                                                                                                          


    ディレクトリ: D:\SandBox\to\from


Mode                LastWriteTime         Length Name                                                                                                                          
----                -------------         ------ ----                                                                                                                          
-a----       2016/09/19     15:31             14 test1.txt                                                                                                                     
-a----       2016/09/19     15:31             14 test2.txt                                                                                                                     



PS D:\SandBox> tree /f D:\SandBox
フォルダー パスの一覧
ボリューム シリアル番号は 4641-823A です
D:\SANDBOX
├─from
│      test1.txt
│      test2.txt
│      
└─to
    │  test1.txt
    │  test2.txt
    │  
    └─from
            test1.txt
            test2.txt
            

PS D:\SandBox> #コピー先ディレクトリの配下に、コピー元フォルダが複製される

回避策

コピー先の有無によって挙動が変わるのは再現性がないため避けたい。 以下のように、ディレクトリコピーではなく、ディレクトリ配下をワイルドカードでコピーするようにすると、フォルダが存在しない場合はエラーとなる。

PS D:\SandBox> Copy-Item .\from\* .\to\ -Recurse -Force -PassThru
Copy-Item : ファイル名、ディレクトリ名、またはボリューム ラベルの構文が間違っています。
発生場所 行:1 文字:1
+ Copy-Item .\from\* .\to\ -Recurse -Force -PassThru
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [Copy-Item], IOException
    + FullyQualifiedErrorId : System.IO.IOException,Microsoft.PowerShell.Commands.CopyItemCommand
 

PS D:\SandBox> md .\to\


    ディレクトリ: D:\SandBox


Mode                LastWriteTime         Length Name                                                                                                                          
----                -------------         ------ ----                                                                                                                          
d-----       2016/09/19     15:46                to                                                                                                                            



PS D:\SandBox> ls


    ディレクトリ: D:\SandBox


Mode                LastWriteTime         Length Name                                                                                                                          
----                -------------         ------ ----                                                                                                                          
d-----       2016/09/19     15:31                from                                                                                                                          
d-----       2016/09/19     15:46                to                                                                                                                            



PS D:\SandBox> Copy-Item .\from\* .\to\ -Recurse -Force -PassThru


    ディレクトリ: D:\SandBox\to


Mode                LastWriteTime         Length Name                                                                                                                          
----                -------------         ------ ----                                                                                                                          
-a----       2016/09/19     15:31             14 test1.txt                                                                                                                     
-a----       2016/09/19     15:31             14 test2.txt                                                                                                                     



PS D:\SandBox> tree /f D:\SandBox
フォルダー パスの一覧
ボリューム シリアル番号は 4641-823A です
D:\SANDBOX
├─from
│      test1.txt
│      test2.txt
│      
└─to
        test1.txt
        test2.txt
        

結論

ディレクトリコピーするときはPowershellを使わないほうが良いと思う(2016/09/19時点)。 なお、このあたりの詳細な挙動については、Copy-Item -Recurse の振舞が詳しい

*1:robocopyコマンドを使ったことがないので確証はない。後日調べる。

PowershellでARPテーブルを表示する(Get-NetNeighbor)

PowershellARPテーブルを確認するときはGet-NetNeighborコマンドレットが使える

環境

PS C:\> #OSのVersion
PS C:\> "$((get-wmiobject win32_operatingsystem).caption) ($((get-wmiobject win32_operatingsystem).version))"
Microsoft Windows 10 Pro (10.0.10586)

PS C:\> #Powershellのバージョン
PS C:\> $PSVersionTable.PSVersion

Major  Minor  Build  Revision
-----  -----  -----  --------
5      0      10586  494     

使用例

PS C:\> Get-NetNeighbor

ifIndex IPAddress                                          LinkLayerAddress     State       PolicyStore    
------- ---------                                          ----------------     -----       -----------    
4       192.0.2.1                                          00-00-5E-00-53-01    Permanent   ActiveStore    

項目の意味

No. Name Meaning
1 ifIndex インターフェースインデックス
2 IPAddress IPアドレス
3 LinkLayerAddress MACアドレス
4 State キャッシュの状態*1
5 PolicyStore キャッシュの種類。
  • ActiveStore:動的(Active)に学習されたキャッシュ。
    一時的なエントリ
  • PersistentStore:静的(Static)に設定したキャッシュ。
    永続的なエントリ

用語

No. Name Meaning
1 the neighbor cache the Address Resolution Protocol (ARP) cacheのこと。
日本語にすると「近隣キャッシュ」らしい。ヘルプでも素直にARPキャッシュと書いてほしい。
2 LinkLayerAddress データリンク層のアドレス、ということでMACアドレスのことを指す。参考:link-layer address

参考

Get-NetNeighbor

また、Powershellのお決まりのコマンド名でARPテーブルの編集も可能。

その他

日本語でググってもコマンドレットが見つからなかったので以下でヘルプを検索した*2

PS C:\> Get-Help ARP

Name                              Category  Module                    Synopsis                                                                                                                                                        
----                              --------  ------                    --------                                                                                                                                                        
Disable-NetAdapterPowerManagement Function  NetAdapter                Disables specific power management features on the network adapter.                                                                                             
Enable-NetAdapterPowerManagement  Function  NetAdapter                Enables specific power management features on the network adapter.                                                                                              
Set-NetAdapterPowerManagement     Function  NetAdapter                Sets the power management properties on the network adapter.                                                                                                    
Get-NetIPInterface                Function  NetTCPIP                  Gets an IP interface.                                                                                                                                           
Get-NetNeighbor                   Function  NetTCPIP                  Gets neighbor cache entries.                                                                                                                                    
Get-NetOffloadGlobalSetting       Function  NetTCPIP                  Gets the global TCP/IP offload settings.                                                                                                                        
Set-NetIPInterface                Function  NetTCPIP                  Modifies an IP interface.                                                                                                                                       
Set-NetOffloadGlobalSetting       Function  NetTCPIP                  Modifies the global TCP/IP offload settings.                                                                                                                    
New-PSSessionConfigurationFile    Cmdlet    Microsoft.PowerShell.Core Creates a file that defines a session configuration.                                                                                                            
Add-Type                          Cmdlet    Microsoft.PowerShell.U... Windows PowerShell セッションに、Microsoft .NET Framework の型 (クラス) を追加します。

例示用のIPアドレスMACアドレスは以下が詳しい

文書に記述する際の説明用/例示用のドメイン名、IPアドレス、MACアドレス RFC6890,RFC5737,RFC3849,RFC2606,RFC6761,RFC7042 IANAなどなど – Abacus Technologies Blog – kana.me 要

追記

2016-09-09 本文中のコマンド名の誤りを修正。

*1:詳細はコマンドのオンラインヘルプに記載ありGet-NetNeighbor

*2:ARPで検索したとき、Get-NetNeighborは表示されるが、New-/Remove-/Set-が表示されないのは正直どうかと思う

中尾ゆうすけ『これだけ!OJT(2010)』

これだけ! OJT

これだけ! OJT

後輩の面倒をみることになったので読んだ。そこそこ面白かった。

以下は引用と適当なコメント

PDCA」を実行するだけで、OJTはほぼうまくいく

OJTのゴールとは?(中略)まずは「自立」が最初の目標になるでしょう。

(中略)「自立」というのは、現在の業務範囲においてOJTリーダーの指示のもと、部下や後輩が自身の判断で業務を支障なく遂行できる状態です。

(中略)成長させるには、どのような業務を通じて必要な能力を身に着けていくのかを、すくなくとも2年から3年という長期的な視点で計画する必要があります。

ひとつの目標に到達したら、その次を目指します。(中略)一段上のステップに目標を置くときが、その人にとってのステップアップのときであり、実際には決してゴールではないのです。 

⇒まったくもってごもっとも。 なのだが、関係ないところでSI業界はそれすらままならない業界なんだなあと思ってしまった。

手持ちの案件に合わせて仕事をすると、どうしても育成はおろそかになってしまうし、かといって全社的な教育が現場に出るにあたって十分かというと… そもそも仕事の内容すらギリギリまで決まらないことも多い。そして何の意味があるか分からないスクショ職人作業を強いられることもある。

OJT計画書をつくります。それによって(中略)共通認識をつくります。関係者が同じ目的・目標をもつということです。

OJT計画書に記載すべき項目を簡単にまとめると以下とのこと。

  1. OJT対象者*1
  2. OJT実施期間
  3. OJT目標*2
  4. 対象者に求める能力、および現状の能力*3
  5. 能力向上施策*4
  6. 研修計画(OFF-JT)*5
  7. 能力評価*6
  8. 長期目標(OJT期間よりも長いスパンの目標。将来像をイメージ)
  9. コメント*7

ビジネスパーソンに必須の3つの能力

⇒以下3点とのこと*8。かっこ書きの中は私の理解。 それぞれの能力に大雑把に計画を立てて、徐々にブラッシュアップしていけばよい。

  • 管理能力(≒マネジメント能力)
  • 対人能力(≒コミュニケーション能力)
  • 専門能力(≒技術力)

目標はレベルとランクを決めて、能力を細分化し段階的に計画すると指導しやすい。

ランク(例);指示のもと⇒独力⇒主体的に

レベル;課内⇒プロジェクト内⇒対顧客

その仕事はなぜあるのか? どうして必要なのか? それを行うことで何になるのか?

⇒上記が仕事の原理・原則であり、これを教えることでOJT対象者は自ら応用して自分で判断しやすくなる。

口頭指示だけでなくマニュアルも用意する。

⇒なかなかできなくて困っている。とりあえず作ってブラッシュアップすればよいと書いてあるが、そもそもその第一版が手間がかかかる。簡単にマニュアルを作る方法はないものか……

*1:誰のための計画書なのか

*2:OJT終了後に対象者が、何のために、どのような能力を、どんなレベルで身に着けるか、を記載する

*3:このギャップが身に着けるべき能力。対象者と指導者の双方が共通認識をもち、合意すること

*4:いつ、どのような業務を通して、誰が指導するかを明記する。OJT目標をより1レベル細分化して記載するとよい

*5:IT業界はこのOFF-JTも豊富なので、業務に必要な能力に応じて活用する

*6:研修の結果を対象者と指導者の双方が確認して次のPLANを立てる

*7:指導者が記載する

*8:ロバート・カッツが提唱