DSCResources/ArcGIS_Service_Account/ArcGIS_Service_Account.psm1
<#
.SYNOPSIS Resource to make the Data Directories accesssible to given Run as Account for a given ArcGIS Component. .PARAMETER Ensure Take the values Present or Absent. - "Present" ensures the Data Directories are accesssible to given Run as Account for a given ArcGIS Component. - "Absent" ensures the Data Directories are made in accesssible to given Run as Account for a given ArcGIS Component(Not Implemented). .PARAMETER Name Name of the ArcGIS Component that is being configured. .PARAMETER RunAsAccount A MSFT_Credential Object - Run as Account, Account to which the DataDir Folders be made accessible .PARAMETER DataDir Data Directory paths to which the necessary permissions need to be given. .PARAMETER IsDomainAccount Is the given RunAsAccount a domain-account? #> function Get-TargetResource { [CmdletBinding()] [OutputType([System.Collections.Hashtable])] param ( [parameter(Mandatory = $true)] [System.String] $Name ) Import-Module $PSScriptRoot\..\..\ArcGISUtility.psm1 -Verbose:$false $null } function Set-TargetResource { [CmdletBinding()] param ( [parameter(Mandatory = $true)] [System.String] $Name, [parameter(Mandatory = $true)] [System.Management.Automation.PSCredential] $RunAsAccount, [System.String[]] $DataDir, [ValidateSet("Present","Absent")] [System.String] $Ensure, [System.Boolean] $IsDomainAccount = $false ) Import-Module $PSScriptRoot\..\..\ArcGISUtility.psm1 -Verbose:$false if($Ensure -ieq 'Present') { $RunAsUserName = $RunAsAccount.UserName $RunAsPassword = $RunAsAccount.GetNetworkCredential().Password $RunAsPassword = $RunAsPassword.Replace('"', '""') Write-Verbose "RunAsAccount Username:- $RunAsUserName" if($RunAsUserName -and $RunAsUserName.StartsWith('.\')){ $RunAsUserName = $RunAsUserName.Substring(2) # Remove the current machine prefix Write-Verbose "Removing the machine prefix for the RunAsAccount to $RunAsUserName" } $RegKey = Get-EsriRegistryKeyForService -ServiceName $Name $InstallDir = (Get-ItemProperty -Path $RegKey -ErrorAction Ignore).InstallDir Write-Verbose "Install Dir for $Name is $InstallDir" if($InstallDir) { $InstallDir = $InstallDir.TrimEnd('\') if(Test-Path $InstallDir) { if(<#$Name -ieq 'ArcGIS Server' -OR $Name -ieq 'Portal for ArcGIS' -or#> $Name -ieq 'ArcGIS Data Store'){ $ExecPath = $InstallDir $Arguments = '/username ' + $($RunAsUserName) + ' /password "' + $($RunAsPassword) +'"' Write-Verbose "Just Checking : - $Arguments" if($Name -ieq 'ArcGIS Server'){ $ExecPath = Join-Path $ExecPath '\\bin\\ServerConfigurationUtility.exe' }elseif($Name -ieq 'Portal for ArcGIS'){ $ExecPath = Join-Path $ExecPath '\\tools\\ConfigUtility\\configureserviceaccount.bat' } elseif($Name -ieq 'ArcGIS Data Store'){ $ExecPath = Join-Path $ExecPath '\\tools\\configureserviceaccount.bat' } Write-Verbose "Providing RunAs Account '$RunAsUserName' has the required permissions to $InstallDir" Write-Verbose "configureserviceaccount $InstallDir /grant $($RunAsUserName):(OI)(CI)F" $psi = New-Object System.Diagnostics.ProcessStartInfo $psi.FileName = $ExecPath $psi.Arguments = $Arguments $psi.UseShellExecute = $false #start the process from it's own executable file $psi.RedirectStandardOutput = $true #enable the process to read from standard output $psi.RedirectStandardError = $true #enable the process to read from standard error $psi.EnvironmentVariables["AGSPORTAL"] = [environment]::GetEnvironmentVariable("AGSPortal","Machine") $psi.EnvironmentVariables["AGSDATASTORE"] = [environment]::GetEnvironmentVariable("AGSDATASTORE","Machine") $psi.EnvironmentVariables["AGSSERVER"] = [environment]::GetEnvironmentVariable("AGSSERVER","Machine") $p = [System.Diagnostics.Process]::Start($psi) $p.WaitForExit() $op = $p.StandardOutput.ReadToEnd() if($op -and $op.Length -gt 0) { Write-Verbose "Output of execution:- $op" } $err = $p.StandardError.ReadToEnd() if($p.ExitCode -eq 0) { Write-Verbose "Initialized correctly indicating successful desktop initialization" $result = $true }else { Write-Verbose "Initialization did not succeed. Process exit code:- $($p.ExitCode) $err" } }else{ if(-not(Test-Acl $InstallDir $RunAsUserName $IsDomainAccount)) { Write-Verbose "Providing RunAs Account '$RunAsUserName' has the required permissions to $InstallDir" Write-Verbose "icacls.exe $InstallDir /grant $($RunAsUserName):(OI)(CI)F" icacls.exe $InstallDir /grant "$($RunAsUserName):(OI)(CI)F" }else { Write-Verbose "RunAs Account '$RunAsUserName' has the required permissions to $InstallDir" } } } } if($DataDir) { foreach($DataDirectory in $DataDir) { if(-not($DataDirectory.StartsWith('\'))){ $LocalPath = $DataDirectory if($LocalPath.StartsWith('HKLM:\')) { $LocalPath = (Get-Item ((Get-ItemProperty ($LocalPath) -ErrorAction Ignore).ContentDir)) }elseif($LocalPath.StartsWith('$env:')){ $LocalPath = $ExecutionContext.InvokeCommand.ExpandString("$DataDirectory") } if($LocalPath -and (Test-Path $LocalPath)) { Write-Verbose "Checking Permissions on $LocalPath" if(-not(Test-Acl $LocalPath $RunAsUserName $IsDomainAccount)) { Write-Verbose "Permissions are not set for $LocalPath" Write-Verbose "Providing RunAs Account '$RunAsUserName' the required permissions to $LocalPath" Write-Verbose "icacls.exe $LocalPath /grant $($RunAsUserName):(OI)(CI)F" icacls.exe $LocalPath /grant "$($RunAsUserName):(OI)(CI)F" } else { Write-Verbose "RunAs Account '$RunAsUserName' has the required permissions to $LocalPath" } } } } } if($Name -ieq 'ArcGISGeoEvent') { ### ### GeoEvent needs additional permissions set and delete zookeeper folder ### $GeoEventProgramData = Join-Path $env:ProgramData 'Esri\GeoEvent' if(Test-Path $GeoEventProgramData) { $ZooKeeperFolder = Join-Path $GeoEventProgramData 'zookeeper' if(Test-Path $ZooKeeperFolder) { Write-Verbose "Deleting ZooKeeper folder $ZooKeeperFolder" Remove-Item -Path $ZooKeeperFolder -Recurse -Force } $ZooKeeperFile = Join-Path $GeoEventProgramData 'zookeeper.properties' if(Test-Path $ZooKeeperFile) { Write-Verbose "Deleting ZooKeeper file $ZooKeeperFile" Remove-Item -Path $ZooKeeperFile -Recurse -Force -ErrorAction Ignore } $ZooKeeperFile = Join-Path $GeoEventProgramData 'zookeeper-dynamic.properties' if(Test-Path $ZooKeeperFile) { Write-Verbose "Deleting ZooKeeper file $ZooKeeperFile" Remove-Item -Path $ZooKeeperFile -Recurse -Force -ErrorAction Ignore } } #Add a check for 10.6 ### ### GeoEvent Clean up - the karaf data folder and ProgramData (after stopping the service) ### GeoEventGateway Clean up - the Gatewaylog and ProgramData (after stopping the service) ### @('ArcGISGeoEvent', 'ArcGISGeoEventGateway') | %{ try { $ServiceName = $_ Write-Verbose "Restarting Service $ServiceName" Stop-Service -Name $ServiceName -Force -ErrorAction Ignore Write-Verbose 'Stopping the service' Wait-ForServiceToReachDesiredState -ServiceName $ServiceName -DesiredState 'Stopped' Write-Verbose 'Stopped the service' }catch { Write-Verbose "[WARNING] Stopping Service $_" } } if(-not($InstallDir)) { # GeoEvent is always installed in Server's install directory $RegKey = Get-EsriRegistryKeyForService -ServiceName 'ArcGIS Server' $InstallDir = (Get-ItemProperty -Path $RegKey -ErrorAction Ignore).InstallDir } if($InstallDir) { $InstallDir = Join-Path $InstallDir 'GeoEvent' $GeoEventKarafDataFolder = Join-Path $InstallDir 'data' $GeoEventGatewayLogFolder = Join-Path $InstallDir 'gateway\log' $GeoEventProgramData = Join-Path $env:ProgramData 'Esri\GeoEvent' $GeoEventGatewayProgramData = Join-Path $env:ProgramData 'Esri\GeoEvent-Gateway' @($GeoEventKarafDataFolder, $GeoEventGatewayLogFolder, $GeoEventProgramData, $GeoEventGatewayProgramData) | %{ $FolderToDelete = $_ Write-Verbose "Clean up Folder:- $FolderToDelete" if(Test-Path $FolderToDelete) { Write-Verbose "Recursively delete Folder:- $FolderToDelete" Get-ChildItem -Path $FolderToDelete | %{ Remove-Item -Path $_.FullName -Recurse -Force -ErrorAction Ignore } } } } ### ### At 10.5 and beyond GE uses the Zookeeper component of ArcGIS Server which stores in local ### $ZooKeeperFolder = Join-Path $env:SystemDrive 'arcgisserver\local\zookeeper' if(Test-Path $ZooKeeperFolder) { Write-Verbose "Deleting ZooKeeper folder $ZooKeeperFolder in local path" Remove-Item -Path $ZooKeeperFolder -Recurse -Force } } <# if(-not($DataDir) -and ($Name -ieq 'Portal for ArcGIS')) { $DataDir = (Get-Item ((Get-ItemProperty ("HKLM:\SOFTWARE\ESRI\$Name")).ContentDir)) } if($DataDir) { $DataDir = $DataDir.TrimEnd('\') if(Test-Path $DataDir) { $acl = Get-Acl $DataDir | Select-Object -ExpandProperty Access | Where {$_.IdentityReference -ieq "$env:ComputerName\$RunAsUserName"} if((-not($acl)) -or ($acl.FileSystemRights -ine 'FullControl') -or ($acl.AccessControlType -ine 'Allow')) { Write-Verbose "Providing RunAs Account '$RunAsUserName' has the required permissiones to $DataDir" Write-Verbose "icacls.exe $DataDir /grant $($RunAsUserName):(OI)(CI)F" icacls.exe $DataDir /grant "$($RunAsUserName):(OI)(CI)F" ## TEMPORARY if($Name -ieq 'Portal for ArcGIS') { $DataDirParent = Split-Path -Path $DataDir -Parent Write-Verbose "Providing RunAs Account '$RunAsUserName' has the required permissiones to $DataDirParent" Write-Verbose "icacls.exe $DataDirParent /grant $($RunAsUserName):(OI)(CI)F" icacls.exe $DataDirParent /grant "$($RunAsUserName):(OI)(CI)F" } }else { Write-Verbose "RunAs Account '$RunAsUserName' has the required permissiones to $DataDir" } } } if($Name -ieq 'ArcGISGeoEvent') { ### ### GeoEvent needs additional permissions set and delete zookeeper folder ### $GeoEventProgramData = Join-Path $env:ProgramData 'Esri\GeoEvent' if(Test-Path $GeoEventProgramData) { Write-Verbose "Program Data Dir for $Name is $GeoEventProgramData" $acl = Get-Acl $GeoEventProgramData | Select-Object -ExpandProperty Access | Where {$_.IdentityReference -ieq "$env:ComputerName\$RunAsUserName"} if((-not($acl)) -or ($acl.FileSystemRights -ine 'FullControl') -or ($acl.AccessControlType -ine 'Allow')) { Write-Verbose "Providing Required Permissions to Program Data Folder for RunAs Account" Write-Verbose "icacls.exe $GeoEventProgramData /grant $($RunAsUserName):(OI)(CI)F" icacls.exe $GeoEventProgramData /grant "$($RunAsUserName):(OI)(CI)F" $ZooKeeperFolder = Join-Path $GeoEventProgramData 'zookeeper' if(Test-Path $ZooKeeperFolder) { Write-Verbose "Deleting ZooKeeper folder $ZooKeeperFolder" Remove-Item -Path $ZooKeeperFolder -Recurse -Force } } else { Write-Verbose 'Permissions to Program Data Folder for RunAs Account are already correct' } } } #> if($Name -ine 'ArcGIS Data Store') # No need to restart DataStore - the mandatory property change for 'failover_on_primary_stop' will take care of it { ### ### If the Service Credentials are changed. Restart the Service (just in case) TODO:- Revisit if this is needed ### try { Write-Verbose "Restarting Service $Name" Stop-Service -Name $Name -Force -ErrorAction Ignore Write-Verbose 'Stopping the service' Wait-ForServiceToReachDesiredState -ServiceName $Name -DesiredState 'Stopped' Write-Verbose 'Stopped the service' }catch { Write-Verbose "[WARNING] Stopping Service $_" } try { Write-Verbose 'Starting the service' Start-Service -Name $Name -ErrorAction Ignore Wait-ForServiceToReachDesiredState -ServiceName $Name -DesiredState 'Running' Write-Verbose "Restarted Service $Name" }catch { Write-Verbose "[WARNING] Starting Service $_" } } }else{ Write-Warning 'Absent not implemented' } } function Test-TargetResource { [CmdletBinding()] [OutputType([System.Boolean])] param ( [parameter(Mandatory = $true)] [System.String] $Name, [parameter(Mandatory = $true)] [System.Management.Automation.PSCredential] $RunAsAccount, [System.String[]] $DataDir, [ValidateSet("Present","Absent")] [System.String] $Ensure, [System.Boolean] $IsDomainAccount = $false ) Import-Module $PSScriptRoot\..\..\ArcGISUtility.psm1 -Verbose:$false $result = $true $RunAsUserName = $RunAsAccount.UserName $RegKey = Get-EsriRegistryKeyForService -ServiceName $Name $InstallDir =(Get-ItemProperty -Path $RegKey -ErrorAction Ignore).InstallDir Write-Verbose "RunAsAccount Username:- $RunAsUserName" if($RunAsUserName -and $RunAsUserName.StartsWith('.\')){ $RunAsUserName = $RunAsUserName.Substring(2) # Remove the current machine prefix Write-Verbose "Removing the machine prefix for the RunAsAccount to $RunAsUserName" } if($InstallDir -and (Test-Path $InstallDir)) { Write-Verbose "Install Dir for $Name is $InstallDir" if(-not(Test-Acl $InstallDir $RunAsUserName $IsDomainAccount)) { Write-Verbose "Permissions are not set for $InstallDir" $result = $false } } if($result) { if($DataDir) { foreach($DataDirectory in $DataDir) { if($result -and -not($DataDirectory.StartsWith('\'))) { $LocalPath = $DataDirectory if($LocalPath.StartsWith('HKLM:\')) { $LocalPath = (Get-Item ((Get-ItemProperty ($LocalPath) -ErrorAction Ignore).ContentDir)) }elseif($LocalPath.StartsWith('$env:')){ $LocalPath = $ExecutionContext.InvokeCommand.ExpandString("$DataDirectory") } if($LocalPath -and (Test-Path $LocalPath)) { Write-Verbose "Checking Permissions on $LocalPath" if(-not(Test-Acl $LocalPath $RunAsUserName $IsDomainAccount)) { Write-Verbose "Permissions are not set for $LocalPath" $result = $false } } } } } } if($Ensure -ieq 'Present') { $result } elseif($Ensure -ieq 'Absent') { (-not($result)) } } function Test-Acl { [CmdletBinding()] [OutputType([System.Boolean])] param ( [parameter(Mandatory = $true)] [System.String] $Directory, [parameter(Mandatory = $true)] [System.String] $RunAsUsername, [System.Boolean] $IsDomainAccount = $false ) $result = $true if(-not($IsDomainAccount)) { $RunAsUserName = "$env:ComputerName\$RunAsUsername" } Write-Verbose "Testing Permission for User $RunAsUserName on Directory $Directory" $acl = Get-Acl $Directory | Select-Object -ExpandProperty Access | Where {$_.IdentityReference -ieq "$RunAsUserName"} | Where {$_.FileSystemRights -ieq "FullControl"} if((-not($acl)) -or ($acl.AccessControlType -ine 'Allow')) { $result = $false } $result } Export-ModuleMember -Function *-TargetResource |