none
PowerShellを用いたプリンタセキュリティへのアクセス権追加について

    質問

  • PowerShellでプリンタのセキュリティ設定を変更したいのですが、

    「Add-AccessControlEntry」が動作しません。

    どのように記述すれば動作するのかご教示ください。

    <やりたい事詳細>

    プリンタプロパティの「セキュリティ」タブで「Everyone」が「印刷:許可」のみ設定

    された状態になっています。

    この状態から、「このプリンターの管理」と「ドキュメントの管理」の権限を追加したいのです。

    ※他のアクセス権はそのまま残したい。

    <記述内容>

    #対象プリンタの指定
    $S = "RICOH RPCS Basic Color"
    $SFilter = "name='" + $S + "'"
    $SPrinter = gwmi win32_printer -filter $SFilter

    #セキュリティ情報を取得
    $ace = $SPrinter.GetSecurityDescriptor

    #'Everyone'のセキュリティ設定を追加
    $ace | Add-AccessControlEntry -Principal Everyone -PrinterRights ManagePrinters  # 983052
    $ace | Add-AccessControlEntry -Principal Everyone -PrinterRights ManageDocuments # 983088
    $ace | Set-SecurityDescriptor

    <実行結果>

    Add-AccessControlEntry : 用語 'Add-AccessControlEntry' は、コマンドレット、関数、スクリプト ファイル、または操作可能な
    プログラムの名前として認識されません。名前が正しく記述されていることを確認し、パスが含まれている場合はそのパスが正しい
    ことを確認してから、再試行してください。
    発生場所 C:\Users\******\Desktop\printer_ace_add.ps1:10 文字:8
    + $ace | Add-AccessControlEntry -Principal Everyone -PrinterRights Mana ...
    +        ~~~~~~~~~~~~~~~~~~~~~~
        + CategoryInfo          : ObjectNotFound: (Add-AccessControlEntry:String) [], CommandNotFoundException
        + FullyQualifiedErrorId : CommandNotFoundException

    Add-AccessControlEntry : 用語 'Add-AccessControlEntry' は、コマンドレット、関数、スクリプト ファイル、または操作可能な
    プログラムの名前として認識されません。名前が正しく記述されていることを確認し、パスが含まれている場合はそのパスが正しい
    ことを確認してから、再試行してください。
    発生場所 C:\Users\******\Desktop\printer_ace_add.ps1:11 文字:8
    + $ace | Add-AccessControlEntry -Principal Everyone -PrinterRights Mana ...
    +        ~~~~~~~~~~~~~~~~~~~~~~
        + CategoryInfo          : ObjectNotFound: (Add-AccessControlEntry:String) [], CommandNotFoundException
        + FullyQualifiedErrorId : CommandNotFoundException

    Set-SecurityDescriptor : 用語 'Set-SecurityDescriptor' は、コマンドレット、関数、スクリプト ファイル、または操作可能な
    プログラムの名前として認識されません。名前が正しく記述されていることを確認し、パスが含まれている場合はそのパスが正しい
    ことを確認してから、再試行してください。
    発生場所 C:\Users\******\Desktop\printer_ace_add.ps1:12 文字:8
    + $ace | Set-SecurityDescriptor
    +        ~~~~~~~~~~~~~~~~~~~~~~
        + CategoryInfo          : ObjectNotFound: (Set-SecurityDescriptor:String) [], CommandNotFoundException
        + FullyQualifiedErrorId : CommandNotFoundException

    <実行環境のバージョン>

    Windows7

    PowerShell2.0

    <参考にしたサイト>

    https://gallery.technet.microsoft.com/scriptcenter/PowerShellAccessControl-d3be7b83#content

    https://docs.microsoft.com/ja-jp/windows/desktop/CIMWin32Prov/setsecuritydescriptor-method-in-class-win32-printer

    2018年7月27日 7:43

回答

  • Examplesのコードだと既存のDACLを完全に上書きしてしまうので、そのような結果になります。

    Everyoneのアクセス権だけを変更するには、DACLに含まれるEveryone以外のACEを保持しておき、新たに生成したEveryoneのACEと結合させてDACLに反映させる必要がありそうです。

    そのようなスクリプトを標準コマンドレットのみで作ってみました。

    $user = "everyone" # 対象ユーザー名
    $printerName = "RICOH RPCS Basic Color" # 対象プリンタ名
    
    $printer = Get-WmiObject -Class win32_printer |
        Where-Object {$_.Name -eq $printerName}
    
    # プリンタのSecurity Descriptorを取得する
    $sd = $printer.GetSecurityDescriptor().Descriptor
    
    # DACLから$user以外のACEを取得する
    $aces = @($sd.DACL | 
        Where-Object {$_.Trustee.Name -ne $user}) 
    
    # ACEオブジェクトを生成する関数
    # see: https://docs.microsoft.com/ja-jp/windows/desktop/CIMWin32Prov/setsecuritydescriptor-method-in-class-win32-printer
    function createAce
    {
        param([string]$user, [int]$accessMask, [int]$type, [int]$flags)
    
        $ace = ([WMIClass] "Win32_Ace").CreateInstance()
        $Trustee = ([WMIClass] "Win32_Trustee").CreateInstance()
        $SID = (new-object security.principal.ntaccount $user).translate([security.principal.securityidentifier])
        [byte[]] $SIDArray = ,0 * $SID.BinaryLength
        $SID.GetBinaryForm($SIDArray,0)
        $Trustee.Name = $user
        $Trustee.SID = $SIDArray
        $ace.AccessMask = $accessMask
        $ace.AceType = $type
        $ace.AceFlags = $flags 
        $ace.Trustee = $Trustee
        return $ace
    }
    
    # $user以外のACEと新しく作った$userのACEを結合
    $aces += 
        @(createAce -user $user -accessMask 983052 -type 0 -flags 0) +  # ManagePrinters 
        @(createAce -user $user -accessMask 983088 -type 0 -flags 9)    # ManageDocuments 
    
    $sd.DACL = [System.Management.ManagementBaseObject[]]$aces # DACLを更新
    $printer.psbase.Scope.Options.EnablePrivileges = $true # 上書き許可
    $ret = $printer.SetSecurityDescriptor($sd).ReturnValue # Security Descriptorを更新
    
    if($ret -eq 0)
    {
        Write-Host "${user}の${printerName}に対するアクセス権を更新しました。"
    }
    else
    {
        Write-Host "アクセス権更新に失敗しました。"
    }



    2018年7月28日 16:58
    モデレータ

すべての返信

  • Add-AccessControlEntry等のコマンドは、標準のものではないので、ご自分でインストールする必要があります。

    まずはScript PowerShellAccessControl Module 3.0/4.0 からzipファイルをダウンロードし、Installation節の内容に従ってモジュールをインストールしてみてください。


    2018年7月27日 8:30
    モデレータ
  • 牟田口様

    回答頂きありがとうございます。

    Σ(゚д゚lll)ガーン インストールが必要なのですね。。。(うわー全然理解できていませんでした。)

    重ねて質問させて頂きたいのですが、

    インストールせずに標準のコマンドレットで実現する術はあるのでしょうか。

    もしもご存知でしたらご教示頂けると大変助かります。


    2018年7月28日 10:05
  • ご自分で示されているサイトの2つ目(SetSecurityDescriptor method of the Win32\_Printer class | Microsoft Docs)のExamplesがまさに、標準のコマンドレットだけで実現する方法なのではないかと思いますが、いかがでしょうか。
    2018年7月28日 12:50
    モデレータ
  • 牟田口様

    ご返信ありがとうございます。

    Examplesのコードを使わせて頂き、処理を行うと
    既存のユーザやグループなどのアクセス権が失われてしまい、
    希望通りの処理になりませんでした。
    また、フルコントロールを指定しても何故かドキュメントの管理はチェックが入らず、これも謎です。
    Everyoneだけいれば別に良いのでは?とも思ったのですが、もしも何かの拍子に消えてしまうと
    面倒な事になりそうなので、他のユーザ等はそのまま残したいのです。

    標準コマンドレットのみで既に存在しているEveryoneのアクセス権の一部を変更するという処理はできないものなのでしょうか・・・。
    (諸事情により別プリンタからセキュリティ内容をコピーする方法は利用できません。)

    2018年7月28日 13:39
  • Examplesのコードだと既存のDACLを完全に上書きしてしまうので、そのような結果になります。

    Everyoneのアクセス権だけを変更するには、DACLに含まれるEveryone以外のACEを保持しておき、新たに生成したEveryoneのACEと結合させてDACLに反映させる必要がありそうです。

    そのようなスクリプトを標準コマンドレットのみで作ってみました。

    $user = "everyone" # 対象ユーザー名
    $printerName = "RICOH RPCS Basic Color" # 対象プリンタ名
    
    $printer = Get-WmiObject -Class win32_printer |
        Where-Object {$_.Name -eq $printerName}
    
    # プリンタのSecurity Descriptorを取得する
    $sd = $printer.GetSecurityDescriptor().Descriptor
    
    # DACLから$user以外のACEを取得する
    $aces = @($sd.DACL | 
        Where-Object {$_.Trustee.Name -ne $user}) 
    
    # ACEオブジェクトを生成する関数
    # see: https://docs.microsoft.com/ja-jp/windows/desktop/CIMWin32Prov/setsecuritydescriptor-method-in-class-win32-printer
    function createAce
    {
        param([string]$user, [int]$accessMask, [int]$type, [int]$flags)
    
        $ace = ([WMIClass] "Win32_Ace").CreateInstance()
        $Trustee = ([WMIClass] "Win32_Trustee").CreateInstance()
        $SID = (new-object security.principal.ntaccount $user).translate([security.principal.securityidentifier])
        [byte[]] $SIDArray = ,0 * $SID.BinaryLength
        $SID.GetBinaryForm($SIDArray,0)
        $Trustee.Name = $user
        $Trustee.SID = $SIDArray
        $ace.AccessMask = $accessMask
        $ace.AceType = $type
        $ace.AceFlags = $flags 
        $ace.Trustee = $Trustee
        return $ace
    }
    
    # $user以外のACEと新しく作った$userのACEを結合
    $aces += 
        @(createAce -user $user -accessMask 983052 -type 0 -flags 0) +  # ManagePrinters 
        @(createAce -user $user -accessMask 983088 -type 0 -flags 9)    # ManageDocuments 
    
    $sd.DACL = [System.Management.ManagementBaseObject[]]$aces # DACLを更新
    $printer.psbase.Scope.Options.EnablePrivileges = $true # 上書き許可
    $ret = $printer.SetSecurityDescriptor($sd).ReturnValue # Security Descriptorを更新
    
    if($ret -eq 0)
    {
        Write-Host "${user}の${printerName}に対するアクセス権を更新しました。"
    }
    else
    {
        Write-Host "アクセス権更新に失敗しました。"
    }



    2018年7月28日 16:58
    モデレータ
  • 牟田口様

    ありがとうございます。すごいです。

    Everyoneに全部チェックが入りました。感動しました。
    本当にありがとうございます。

    ドキュメントの管理を有効にする際、AceFlagsを9にされていると思いますが、
    この値はどのように取得されたものなのでしょうか。調べてみたのですが、見つけられず、
    何か参考ページ等ございましたらご教示頂けると有難いです。

    2018年7月29日 11:46
  • 実は、プリンタのプロパティダイアログを使い、手動でアクセス許可の「ドキュメントの管理」を「許可」にした際、生成するACEのAccessMask値とAceFlags値をそのまま使っているだけだったりします。

    AceFlags=9は、OBJECT_INHERIT_ACE (0x01)とINHERIT_ONLY_ACE (0x08)のビット論理和で、子オブジェクトのみにACEを適用するという意味になります。

    ちなみにAceFlags=0は適用先が「このオブジェクトのみ」になります。

    参考:Win32\_ACE class | Microsoft Docs

    2018年7月29日 12:36
    モデレータ
  • 牟田口様

    解説頂きありがとうございます。
    この度はありがとうございました。
    2018年7月30日 3:58