DSCResources/ArcGIS_EGDB/ArcGIS_EGDB.psm1
|
$modulePath = Join-Path -Path (Split-Path -Path (Split-Path -Path $PSScriptRoot -Parent) -Parent) -ChildPath 'Modules' # Import the ArcGIS Common Modules Import-Module -Name (Join-Path -Path $modulePath ` -ChildPath (Join-Path -Path 'ArcGIS.Common' ` -ChildPath 'ArcGIS.Common.psm1')) Import-Module -Name (Join-Path -Path $modulePath ` -ChildPath (Join-Path -Path 'ArcGIS.Client' ` -ChildPath 'ArcGIS.Client.Server.psm1')) <# .SYNOPSIS Configures a Refrenced or Managed Geo Database .PARAMETER Ensure Indicates if the GeoDatabase should be configured or not. Take the values Present or Absent. - "Present" ensures that GeoDatabase is Configured with a server whether as a refrenced or Managed one. - "Absent" ensures that GeoDatabase is Un-Configured i.e. when present (Not Implemented). .PARAMETER DatabaseServer Host Name of the Machine on which the GeoDatabase is installed and Configured. .PARAMETER DatabaseName Name of the GeoDatabase .PARAMETER ServerSiteAdministrator A MSFT_Credential Object - Primary site administrator of the Server to register the GeoDatabase. .PARAMETER DatabaseServerAdministrator A MSFT_Credential Object - Database Admin User .PARAMETER SDEUser A MSFT_Credential Object - A SDE User .PARAMETER DatabaseUser A MSFT_Credential Object - A Geo-Database User .PARAMETER IsManaged Boolean to Indicate if the GeoDatabase is Managed. .PARAMETER EnableGeodatabase Boolean parameter to Indicate Enabling of a Geo-Database. .PARAMETER DatabaseType Type of Database Product used to install the GeoDatabase #> function Get-TargetResource { [CmdletBinding()] [OutputType([System.Collections.Hashtable])] param ( [parameter(Mandatory = $true)] [System.String] $DatabaseServer, [parameter(Mandatory = $true)] [System.String] $DatabaseName ) $returnValue = @{ DatabaseServer = $DatabaseServer DatabaseName = $DatabaseName } $returnValue } function Set-TargetResource { [CmdletBinding()] param ( [parameter(Mandatory = $true)] [System.String] $DatabaseServer, [parameter(Mandatory = $true)] [System.String] $DatabaseName, [parameter(Mandatory = $true)] [PSCredential] $ServerSiteAdministrator, [parameter(Mandatory = $true)] [PSCredential] $DatabaseServerAdministrator, [parameter(Mandatory = $false)] [PSCredential] $SDEUser, [parameter(Mandatory = $true)] [PSCredential] $DatabaseUser, [parameter(Mandatory = $true)] [System.Boolean] $IsManaged, [parameter(Mandatory = $true)] [System.Boolean] $EnableGeodatabase, [parameter(Mandatory = $true)] [ValidateSet("SQLServerDatabase","AzureSQLDatabase","AzureMISQLDatabase","AzureFlexiblePostgreSQLDatabase","AWSRDSPostgreSQLDatabase","AWSAuroraPostgreSQLDatabase")] [System.String] $DatabaseType, [ValidateSet("Present","Absent")] [System.String] $Ensure ) if($Ensure -ieq 'Present') { $ServerBaseUrl = Get-ArcGISComponentBaseUrl -ComponentName "Server" Write-Verbose "Waiting for '$($ServerBaseUrl)' to intialize" Test-ArcGISComponentHealth -BaseURL $ServerBaseUrl -ComponentName "Server" -Verbose $Referer = $ServerBaseUrl Write-Verbose "Retrieve token for site admin $($ServerSiteAdministrator.UserName)" $token = Get-ServerToken -URL $ServerBaseUrl -Referer $Referer -Credential $ServerSiteAdministrator Write-Verbose "Ensure the Publishing GP Service (Tool) is started on Server" $PublishingToolsPath = 'System/PublishingTools.GPServer' [int]$NumAttempts = 0 [bool]$Done = $False while(-not($Done) -and ($NumAttempts -lt 10)) { Write-Verbose "Sleeping for 1 minutes for the Publishing Service to start" Start-Sleep -Seconds 60 $serviceStatus = Invoke-GPServiceOperation -URL $ServerBaseUrl -Token $token.token -Referer $Referer -ServicePath $PublishingToolsPath -OperationName "status" Write-Verbose "Service Status :- $serviceStatus" if($serviceStatus.configuredState -ine 'STARTED' -or $serviceStatus.realTimeState -ine 'STARTED') { Write-Verbose "Starting Service $PublishingToolsPath" Invoke-GPServiceOperation -URL $ServerBaseUrl -Token $token.token -Referer $Referer -ServicePath $PublishingToolsPath -OperationName "start" }else{ Write-Verbose "Service $PublishingToolsPath are started." break; } $NumAttempts++ } $IsPostgres = @("AzureFlexiblePostgreSQLDatabase","AWSRDSPostgreSQLDatabase","AWSAuroraPostgreSQLDatabase") -icontains $DatabaseType $SdeUserName = "sde" $SdeUserPasswordSecureObject = if($SDEUser){ $SDEUser.Password }else{ $DatabaseUser.Password } $SDECredential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList ($SdeUserName, $SdeUserPasswordSecureObject ) if($IsPostgres){ Import-Module -Name (Join-Path $PSScriptRoot 'ArcGIS_EGDB.PostgreSQL.psm1') Invoke-CreatePostgreSQLSDEIfNotExist -DatabaseType $DatabaseType -DatabaseServer $DatabaseServer ` -DatabaseName $DatabaseName -DatabaseServerAdministrator $DatabaseServerAdministrator ` -SDECredential $SDECredential -DatabaseUser $DatabaseUser ` -EnableGeodatabase $EnableGeodatabase -Verbose }else{ Import-Module -Name (Join-Path $PSScriptRoot 'ArcGIS_EGDB.MSSQL.psm1') Invoke-CreateMSSQLSDEIfNotExist -DatabaseType $DatabaseType -DatabaseServer $DatabaseServer ` -DatabaseName $DatabaseName -DatabaseServerAdministrator $DatabaseServerAdministrator ` -SDECredential $SDECredential -DatabaseUser $DatabaseUser -Verbose } try { $DBType = if($IsPostgres){ "POSTGRESQL" }else{ "SQLSERVER" } $ServerRegValue = Get-ArcGISComponentVersionAndInstallDirectory -ComponentName 'Server' $RealVersion = $ServerRegValue.RealVersion $RealVersionArr = $RealVersion.Split(".") $Version = $RealVersionArr[0] + '.' + $RealVersionArr[1] $InstallDir = $ServerRegValue.InstallDir Write-Verbose "RealVersion of ArcGIS Software Installed:- $RealVersion" $PythonInstallDir = Join-Path $InstallDir "\\framework\\runtime\\ArcGIS\\bin\\Python\\envs\\arcgispro-py3" $PythonPath = ((Get-ChildItem -Path $PythonInstallDir -Filter 'python.exe' -Recurse -File) | Select-Object -First 1 -ErrorAction Ignore) if($null -eq $PythonPath) { throw "Python not found on machine. Please install Python." } $PythonInterpreterPath = $PythonPath.FullName if($EnableGeodatabase) { $PythonScriptFileName = 'enable_enterprise_gdb_3x.py' $PythonScriptPath = Join-Path $PSScriptRoot $PythonScriptFileName if(-not(Test-Path $PythonScriptPath)){ throw "$PythonScriptPath not found" } $LicenseFilePath = "$env:SystemDrive\Program Files\ESRI\License$($Version)\sysgen\keycodes" if(-not (Test-Path $LicenseFilePath)) { throw "License file not found at expected location $LicenseFilePath" } ## Having a space in the path to the license file causes issue ## Copy the file temporarily to root of the system drive $TempFolderPath = Join-Path "$env:SystemDrive\ArcGIS\Deployment" 'Temp' if(-not(Test-Path $TempFolderPath)) { Write-Verbose "Creating folder $TempFolderPath" New-Item $TempFolderPath -ItemType directory -Force } Copy-Item -Path $LicenseFilePath -Destination (Join-Path $TempFolderPath 'licensecopytemp.ecp') -Force $LicenseFilePath = (Join-Path $TempFolderPath 'licensecopytemp.ecp') Write-Verbose "Temp copy of license $LicenseFilePath" if(-not (Test-Path $LicenseFilePath)) { throw "License file that was copied was not found at expected location $LicenseFilePath" } Write-Verbose 'Enabling Geodatabase' $SdeConnectUserName = $SdeUserName $Arguments = " ""$PythonScriptPath"" --DBMS $DBType -s $DatabaseServer -d $DatabaseName -u $SdeConnectUserName -p $($SDECredential.GetNetworkCredential().Password) -l $LicenseFilePath" $RedactedArguments = " ""$PythonScriptPath"" --DBMS $DBType -s $DatabaseServer -d $DatabaseName -u $SdeConnectUserName -p xxxxx -l $LicenseFilePath" Write-Verbose "[Running Command] $PythonInterpreterPath $RedactedArguments " $StdOutLogFile = [System.IO.Path]::GetTempFileName() $StdErrLogFile = [System.IO.Path]::GetTempFileName() Start-Process -FilePath $PythonInterpreterPath -ArgumentList $Arguments -RedirectStandardError $StdErrLogFile -RedirectStandardOutput $StdOutLogFile -Wait Write-Verbose "$StdOutLogFile" $StdOut = Get-Content $StdOutLogFile -Raw if($null -ne $StdOut -and $StdOut.Length -gt 0) { Write-Verbose $StdOut } if($StdOut -icontains 'ERROR') { throw "Error Enabling Geodatabase. StdOut Error:- $StdOut"} [string]$StdErr = Get-Content $StdErrLogFile -Raw if($null -ne $StdErr -and $StdErr.Length -gt 0) { Write-Verbose "[ERROR] $StdErr" } if($StdErr -icontains 'ERROR') { throw "Error Enabling Geodatabase. StdErr Error:- $StdErr"} Remove-Item $StdOutLogFile -Force -ErrorAction Ignore Remove-Item $StdErrLogFile -Force -ErrorAction Ignore } #region Create Connection file $OpFolder = $env:TEMP $DatabaseUserName = $DatabaseUser.UserName $OpFile = "$($DatabaseServer)_$($DatabaseName)_$($DatabaseUserName).sde" $SDEFile = Join-Path $OpFolder $OpFile $PythonScriptFileName = 'create_connection_file_3x.py' $PythonScriptPath = Join-Path $PSScriptRoot $PythonScriptFileName if(-not(Test-Path $PythonScriptPath)){ throw "$PythonScriptPath not found" } $Arguments = " ""$PythonScriptPath"" --DBMS $DBType -s $DatabaseServer -d $DatabaseName -u $DatabaseUserName -p $($DatabaseUser.GetNetworkCredential().Password) -o $OpFolder -f $OpFile" $RedactedArguments = " ""$PythonScriptPath"" --DBMS $DBType -s $DatabaseServer -d $DatabaseName -u $DatabaseUserName -p xxxx -o $OpFolder -f $OpFile" Write-Verbose "[Running Command] $PythonInterpreterPath $RedactedArguments" $StdOutLogFile = [System.IO.Path]::GetTempFileName() $StdErrLogFile = [System.IO.Path]::GetTempFileName() Start-Process -FilePath $PythonInterpreterPath -ArgumentList $Arguments -RedirectStandardError $StdErrLogFile -RedirectStandardOutput $StdOutLogFile -Wait $StdOut = Get-Content $StdOutLogFile -Raw if($null -ne $StdOut -and $StdOut.Length -gt 0) { Write-Verbose $StdOut } $SDELogContents = $null if($IsPostgres){ $SDELogFilePath = Join-Path $env:Temp 'sde_setup' #check }else{ $SDELogFilePath = Join-Path $env:Temp 'sdedc_SQL Server' } if(Test-Path $SDELogFilePath) { $SDELogContents = (Get-Content $SDELogFilePath -Raw) Write-Verbose $SDELogContents } #if($SDELogContents -and $SDELogContents.IndexOf('Fail') -gt -1){ # throw "[ERROR] $SDELogContents" #} if($StdOut -and ($StdOut.IndexOf('ERROR') -gt -1)) { throw "Error Creating Connection File. StdOut Error:- $StdOut"} $StdErr = Get-Content $StdErrLogFile -Raw if($null -ne $StdErr -and $StdErr.Length -gt 0) { Write-Verbose "[ERROR] $StdErr" } if($StdErr -icontains 'ERROR') { throw "Error Creating Connection File. StdErr Error:- $StdErr"} Remove-Item $StdOutLogFile -Force -ErrorAction Ignore Remove-Item $StdErrLogFile -Force -ErrorAction Ignore #endregion $dataItems = Get-ArcGISEGDBDataItems -URL $ServerBaseUrl -Token $token.token -Referer $Referer $dataItemForDatabase = $dataItems | Where-Object { $DatabaseServer -ieq $_.SERVER -and $DatabaseName -ieq $_.DATABASE } if(-not($dataItemForDatabase)) { Write-Verbose "Item for database '$DatabaseName' in Server '$DatabaseServer' is NOT registered. Registering now." Register-EGDBWithServerSite -URL $ServerBaseUrl -SDEFilePath $SDEFile ` -Server $DatabaseServer -Database $DatabaseName ` -Token $token.token -Referer $Referer ` -IsManaged $IsManaged }else { Write-Verbose "Item for database '$DatabaseName' in Server '$DatabaseServer' is already registered" } } finally { ## ## Remove License File ## if($LicenseFilePath -and (Test-Path $LicenseFilePath)) { Write-Verbose "Removing License File $LicenseFilePath" Remove-Item $LicenseFilePath -ErrorAction Ignore | Out-Null } ## ## Remove .sde file ## if($null -ne $SDEFile -and $SDEFile.Length -gt 0 -and (Test-Path $SDEFile)) { Write-Verbose "Removing SDEFile $SDEFile" Remove-Item $SDEFile -ErrorAction Ignore | Out-Null } if($TempFolderPath -and $TempFolderPath.Length -gt 0 -and (Test-Path $TempFolderPath)) { Write-Verbose "Removing TempFolder $TempFolderPath" Remove-Item $TempFolderPath -ErrorAction Ignore | Out-Null } } } elseif($Ensure -ieq 'Absent') { Write-Warning "Absent has not been implemented" } } function Test-TargetResource { [CmdletBinding()] [OutputType([System.Boolean])] param ( [parameter(Mandatory = $true)] [System.String] $DatabaseServer, [parameter(Mandatory = $true)] [System.String] $DatabaseName, [parameter(Mandatory = $true)] [PSCredential] $ServerSiteAdministrator, [parameter(Mandatory = $true)] [PSCredential] $DatabaseServerAdministrator, [parameter(Mandatory = $false)] [PSCredential] $SDEUser, [parameter(Mandatory = $true)] [PSCredential] $DatabaseUser, [parameter(Mandatory = $true)] [System.Boolean] $IsManaged, [parameter(Mandatory = $true)] [System.Boolean] $EnableGeodatabase, [parameter(Mandatory = $true)] [ValidateSet("SQLServerDatabase","AzureSQLDatabase","AzureMISQLDatabase","AzureFlexiblePostgreSQLDatabase","AWSRDSPostgreSQLDatabase","AWSAuroraPostgreSQLDatabase")] [System.String] $DatabaseType, [ValidateSet("Present","Absent")] [System.String] $Ensure ) $result = $false $Referer = 'https://localhost:6443/' [System.Reflection.Assembly]::LoadWithPartialName("System.Web") | Out-Null $ServerBaseUrl = Get-ArcGISComponentBaseUrl -ComponentName "Server" Write-Verbose "Waiting for '$($ServerBaseUrl)' to intialize" Test-ArcGISComponentHealth -BaseURL $ServerBaseUrl -ComponentName "Server" -Verbose $token = Get-ServerToken -URL $ServerBaseUrl -Referer $Referer -Credential $ServerSiteAdministrator if(($Ensure -ieq 'Present') -and (!$token.token)) { throw "Unable to retrieve token for user '$($ServerSiteAdministrator.UserName)'. Please enter valid credentials for the server site administrator" } # Check if database name is mixed. Not supported by ArcGIS if($DatabaseName -cne $DatabaseName.ToLower()){ throw "Uppercase and mixed-case object names are not supported for geodatabases in PostgreSQL." } # $DatabaseServerToCheck = if($IsManaged) { $null } else { $DatabaseServer } # $DatabaseNameToCheck = if($IsManaged) { $null } else { $DatabaseName } $dataItems = Get-ArcGISEGDBDataItems -URL $ServerBaseUrl -Token $token.token -Referer $Referer $dataItemForDatabase = $dataItems | Where-Object { $DatabaseServer -ieq $_.SERVER -and $DatabaseName -ieq $_.DATABASE } if($IsManaged) { Write-Verbose "Server can have only 1 managed database. Verify this" $managedDatabaseItem = $dataItems | Where-Object { $_.isManaged } if($dataItemForDatabase -and ($managedDatabaseItem.id -ieq $dataItemForDatabase.id)) { Write-Verbose "Data Item exists and is the managed database" $result = $true # Item exists and is the managed database }elseif($managedDatabaseItem -and ($managedDatabaseItem.id -ine $dataItemForDatabase.id)) { throw "A Managed Database with Server '$($managedDatabaseItem.SERVER)' and Database '$($managedDatabaseItem.DATABASE)' is already registered with id '$($managedDatabaseItem.id)'" } }else { Write-Verbose "Server can have multiple unmanaged database. Check if this database is already registered as an item" if($dataItemForDatabase) { Write-Verbose "Data Item already exists for this database" $result = $true }else { Write-Verbose "Data Item does not exist for this database" } } if($Ensure -ieq 'Present') { $result } elseif($Ensure -ieq 'Absent') { (-not($result)) } } function Get-ArcGISEGDBDataItems { [CmdletBinding()] param( [System.String] $URL, [string] $Token, [System.String] $Referer ) $items = Find-DataItems -URL $URL -Token $Token -Referer $Referer -Verbose $DataItems = @() foreach($item in $items) { $DataItem = @{ id = $item.id; isManaged = $item.info.isManaged } if($item.info.connectionString) { $ConnStringSplits = $item.info.connectionString.Split(';') foreach($ConnStringSplit in $ConnStringSplits) { $KeyValuePairSplits = $ConnStringSplit.Split('=') $Key = $KeyValuePairSplits[0] if($Key -and $KeyValuePairSplits.Length -gt 1) { $Value = $KeyValuePairSplits[1] $DataItem.Add($Key, $Value) } } } $DataItems += $DataItem } $DataItems } function Register-EGDBWithServerSite { [CmdletBinding()] param( [System.String] $URL, [System.String] $SiteName, [System.String] $SDEFilePath, [System.String] $Server, [System.String] $Database, [System.String] $Token, [System.String] $Referer, [System.Boolean] $IsManaged ) [System.Reflection.Assembly]::LoadWithPartialName("System.Net") | Out-Null ### ### Check that the system publishing tool is available ### $serviceStatus = Invoke-GPServiceOperation -URL $URL -Token $Token -Referer $Referer -ServicePath "System/PublishingTools.GPServer" -OperationName "status" Write-Verbose "Service Status :- $serviceStatus" if($null -ne $serviceStatus.status.error) { throw "Error checking System Publishing Tool:- $($serviceStatus.status.error.messages)" } if($serviceStatus.configuredState -ne 'STARTED' -or $serviceStatus.realTimeState -ne 'STARTED') { throw "Publishing Tools GP Server not in STARTED State. Configured State:- $($serviceStatus.configuredState), Realtime State:- $($serviceStatus.realTimeState)" } [string]$UploadItemUrl = $URL.TrimEnd('/') + '/admin/uploads/upload' Write-Verbose "Uploading File $SDEFilePath to $UploadItemUrl" $res = Invoke-UploadFile -url $UploadItemUrl -filePath $SDEFilePath -fileContentType 'application/octet-stream' ` -formParams @{ token = $Token; f = 'json' } -Referer $Referer -fileParameterName "itemFile" $response = $res | ConvertFrom-Json Write-Verbose ($res) if($response.status -ieq "error") { throw "Error uploading .sde file. Error:- $($response.messages | ConvertTo-Json -Depth 5)" } $ItemId = $response.item.itemID ### ### Submit a job to to the 'Get Database Connection' GP Tool ### [string]$ConnString = Invoke-GetDatabaseConnectionGPTool -URL $URL -Token $Token -Referer $Referer -ConnectionFileItemId $ItemId -Verbose ## ## Validating Data Item ## $ItemConnectionObject = @{ type = 'egdb' info = @{ dataStoreConnectionType = if($IsManaged){'serverOnly'}else{'shared'} isManaged = $IsManaged connectionString = $ConnString } path = "/enterpriseDatabases/$($Server)_$($Database)" } Invoke-DataStoreItemOperation -URL $URL -Token $Token -Referer $Referer -ConnectionObject $ItemConnectionObject -OperationName "validateDataItem" -Verbose ## ## Registering Data Item ## Invoke-DataStoreItemOperation -URL $URL -Token $Token -Referer $Referer -ConnectionObject $ItemConnectionObject -OperationName "registerItem" -Verbose } Export-ModuleMember -Function *-TargetResource |