deploy/scripts/pwsh/SAPDeploymentUtilities/Internal/new_sapdeployment.ps1 (476 lines of code) (raw):
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
function New-SAPSystem {
<#
.SYNOPSIS
Deploy a new system
.DESCRIPTION
Deploy a new system
.PARAMETER Parameterfile
This is the parameter file for the system
.PARAMETER Type
This is the type of the system, valid values are sap_deployer, sap_library, sap_landscape, sap_system
.PARAMETER DeployerStateFileKeyName
This is the optional Deployer state file name
.PARAMETER LandscapeStateFileKeyName
This is the optional Landscape state file name
.PARAMETER StorageAccountName
This is the optional terraform state file storage account name
.EXAMPLE
#
#
# Import the module
Import-Module "SAPDeploymentUtilities.psd1"
New-SAPSystem -Parameterfile .\DEV-WEEU-SAP00-X00.json -Type sap_system
.EXAMPLE
#
#
# Import the module
Import-Module "SAPDeploymentUtilities.psd1"
New-SAPSystem -Parameterfile .\DEV-WEEU-SAP00-X00.json -Type sap_system -DeployerStateFileKeyName MGMT-WEEU-DEP00-INFRASTRUCTURE.terraform.tfstate -LandscapeStateFileKeyName DEV-WEEU-SAP01-INFRASTRUCTURE.terraform.tfstate
.EXAMPLE
#
#
# Import the module
Import-Module "SAPDeploymentUtilities.psd1"
New-SAPSystem -Parameterfile .\MGMT-WEEU-SAP_LIBRARY.json -Type sap_library
.LINK
https://github.com/Azure/sap-automation
.NOTES
v0.1 - Initial version
.
#>
<#
Copyright (c) Microsoft Corporation.
Licensed under the MIT license.
#>
[cmdletbinding(SupportsShouldProcess)]
param(
#Parameter file
[Parameter(Mandatory = $true)][string]$Parameterfile ,
[Parameter(Mandatory = $true)][SAP_Types]$Type,
[Parameter(Mandatory = $false)][string]$DeployerStateFileKeyName,
[Parameter(Mandatory = $false)][string]$LandscapeStateFileKeyName,
[Parameter(Mandatory = $false)][string]$StorageAccountName,
[Parameter(Mandatory = $false)][Switch]$Force,
[Parameter(Mandatory = $false)][Switch]$Silent
)
Write-Host -ForegroundColor green ""
Write-Host -ForegroundColor green "Deploying the" $Type
if ($true -eq $Force) {
Remove-Item ".terraform" -ErrorAction SilentlyContinue -Recurse
Remove-Item "terraform.tfstate" -ErrorAction SilentlyContinue
Remove-Item "terraform.tfstate.backup" -ErrorAction SilentlyContinue
}
$autoApprove = ""
if ($Silent) {
$autoApprove = " --auto-approve "
}
$CachePath = (Join-Path -Path $Env:APPDATA -ChildPath "terraform.d\plugin-cache")
if ( -not (Test-Path -Path $CachePath)) {
New-Item -Path $CachePath -ItemType Directory
}
$env:TF_PLUGIN_CACHE_DIR = $CachePath
$curDir = (Get-Location)
Add-Content -Path "deployment.log" -Value ("Deploying the: " + $Type)
Add-Content -Path "deployment.log" -Value (Get-Date -Format "yyyy-MM-dd HH:mm")
$fInfo = Get-ItemProperty -Path $Parameterfile
if ($false -eq $fInfo.Exists ) {
Write-Error ("File " + $Parameterfile + " does not exist")
Add-Content -Path "deployment.log" -Value ("File " + $Parameterfile + " does not exist")
return
}
$ParamFullFile = (Get-ItemProperty -Path $Parameterfile -Name Fullname).Fullname
$mydocuments = [environment]::getfolderpath("mydocuments")
$filePath = $mydocuments + "\sap_deployment_automation.ini"
$iniContent = Get-IniContent -Path $filePath
$changed = $false
if ($Parameterfile.StartsWith(".\")) {
if ($Parameterfile.Substring(2).Contains("\")) {
Write-Error "Please execute the script from the folder containing the json file and not from a parent folder"
Add-Content -Path "deployment.log" -Value "Please execute the script from the folder containing the json file and not from a parent folder"
return;
}
}
$extra_vars = " "
if ( (Test-Path -Path "terraform.tfvars")) {
$extra_vars = " -var-file=" + (Join-Path -Path $curDir -ChildPath "terraform.tfvars")
}
$key = $fInfo.Name.replace($fInfo.Extension, ".terraform.tfstate")
$landscapeKey = ""
if ($Type -eq "sap_landscape") {
$landscapeKey = $key
}
$ctx = Get-AzContext
if ($null -eq $ctx) {
Connect-AzAccount
}
$sub = $env:ARM_SUBSCRIPTION_ID
$Environment = ""
$region = ""
$KeyValuePairs = @{}
if ($fInfo.Extension -eq ".tfvars") {
$paramContent = Get-Content -Path $Parameterfile
foreach ($param in $paramContent) {
if ($param.Contains("=")) {
$KeyValuePairs.Add($param.Split("=")[0].ToLower(), $param.Split("=")[1].Replace("""", ""))
}
}
$Environment = $KeyValuePairs["environment"]
$region = $KeyValuePairs["location"]
}
else {
$jsonData = Get-Content -Path $Parameterfile | ConvertFrom-Json
$Environment = $jsonData.infrastructure.environment
$region = $jsonData.infrastructure.region
}
$combined = $Environment + $region
$spn_kvSpecified = $jsonData.key_vault.kv_spn_id.Length -gt 0
$changed = $false
if ($null -eq $iniContent[$combined]) {
Select-AzSubscription -SubscriptionId $env:ARM_SUBSCRIPTION_ID
Write-Error "The Terraform state information is not available"
$saName = Read-Host -Prompt "Please specify the storage account name for the terraform storage account"
$rID = Get-AzResource -Name $saName
$rgName = $rID.ResourceGroupName
$tfstate_resource_id = $rID.ResourceId
if ($Type -eq "sap_system") {
if ($null -ne $LandscapeStateFileKeyName) {
$landscape_tfstate_key = $LandscapeStateFileKeyName
}
else {
$landscape_tfstate_key = Read-Host -Prompt "Please enter the landscape statefile for the deployment"
}
if ($Type -eq "sap_landscape") {
$iniContent[$combined].Landscape = $landscapeKey
}
$changed = $true
}
else {
$Category1 = @{"REMOTE_STATE_RG" = $rgName; "REMOTE_STATE_SA" = $saName; "tfstate_resource_id" = $tfstate_resource_id }
$iniContent += @{$combined = $Category1 }
if ($Type -eq "sap_landscape") {
$iniContent[$combined].Landscape = $landscapeKey
}
$changed = $true
}
}
else {
$tfstate_resource_id = $iniContent[$combined]["tfstate_resource_id"]
$saName = $iniContent[$combined]["REMOTE_STATE_SA"]
$rgName = $iniContent[$combined]["REMOTE_STATE_RG"]
$sub = $iniContent[$combined]["STATE_SUBSCRIPTION"]
if ($Type -eq "sap_system") {
if ($null -ne $LandscapeStateFileKeyName -and "" -ne $LandscapeStateFileKeyName) {
$landscape_tfstate_key = $LandscapeStateFileKeyName
$iniContent[$combined].Landscape = $LandscapeStateFileKeyName
$changed = $true
}
else {
$landscape_tfstate_key = $iniContent[$combined].Landscape
}
}
}
if ($null -ne $sub -and "" -ne $sub) {
if ( $sub -ne $env:ARM_SUBSCRIPTION_ID) {
Select-AzSubscription -SubscriptionId $sub
}
}
else {
$sub = $env:ARM_SUBSCRIPTION_ID
}
if ("sap_deployer" -eq $Type) {
$iniContent[$combined]["Deployer"] = $key.Trim()
$deployer_tfstate_key = $key
$changed = $true
}
else {
if ($null -ne $DeployerStateFileKeyName -and "" -ne $DeployerStateFileKeyName) {
$deployer_tfstate_key = $DeployerStateFileKeyName
$iniContent[$combined]["Deployer"] = $deployer_tfstate_key.Trim()
$changed = $true
}
else {
$deployer_tfstate_key = $iniContent[$combined]["Deployer"]
}
}
if ($null -ne $StorageAccountName -and "" -ne $StorageAccountName) {
$saName = $StorageAccountName
$rID = Get-AzResource -Name $saName
$rgName = $rID.ResourceGroupName
$tfstate_resource_id = $rID.ResourceId
$iniContent[$combined]["REMOTE_STATE_RG"] = $rgName
$iniContent[$combined]["REMOTE_STATE_SA"] = $saName
$iniContent[$combined]["tfstate_resource_id"] = $tfstate_resource_id
$changed = $true
}
else {
$saName = $iniContent[$combined]["REMOTE_STATE_SA"].trim()
$rgName = $iniContent[$combined]["REMOTE_STATE_RG"]
$tfstate_resource_id = $iniContent[$combined]["tfstate_resource_id"]
}
if ($null -eq $saName -or "" -eq $saName) {
Select-AzSubscription -SubscriptionId $env:ARM_SUBSCRIPTION_ID
$saName = Read-Host -Prompt "Please specify the storage account name for the terraform storage account"
$rID = Get-AzResource -Name $saName
$rgName = $rID.ResourceGroupName
$tfstate_resource_id = $rID.ResourceId
if ($null -ne $tfstate_resource_id) {
$sub = $tfstate_resource_id.Split("/")[2]
}
$iniContent[$combined]["REMOTE_STATE_SA"] = $saName
$iniContent[$combined]["REMOTE_STATE_RG"] = $rgName
$iniContent[$combined]["tfstate_resource_id"] = $tfstate_resource_id
$iniContent[$combined]["STATE_SUBSCRIPTION"] = $sub
$changed = $true
if ($changed) {
Out-IniFile -InputObject $iniContent -Path $filePath
}
$changed = $false
}
else {
$rgName = $iniContent[$combined]["REMOTE_STATE_RG"]
$tfstate_resource_id = $iniContent[$combined]["tfstate_resource_id"]
}
if ($null -eq $tfstate_resource_id -or "" -eq $tfstate_resource_id) {
$rID = Get-AzResource -Name $saName
$rgName = $rID.ResourceGroupName
$tfstate_resource_id = $rID.ResourceId
if ($null -ne $tfstate_resource_id) {
$sub = $tfstate_resource_id.Split("/")[2]
}
$iniContent[$combined]["REMOTE_STATE_SA"] = $saName
$iniContent[$combined]["REMOTE_STATE_RG"] = $rgName
$iniContent[$combined]["tfstate_resource_id"] = $tfstate_resource_id
$iniContent[$combined]["STATE_SUBSCRIPTION"] = $sub
$changed = $true
if ($changed) {
Out-IniFile -InputObject $iniContent -Path $filePath
}
$changed = $false
}
else {
if ($null -ne $tfstate_resource_id) {
$sub = $tfstate_resource_id.Split("/")[2]
}
}
$repo = $iniContent["Common"]["repo"]
if ($Type -eq "sap_system") {
if ($null -eq $landscape_tfstate_key -or "" -eq $landscape_tfstate_key) {
$landscape_tfstate_key = Read-Host -Prompt "Please enter the landscape statefile for the deployment"
if ($Type -eq "sap_system") {
$iniContent[$combined]["Landscape"] = $landscape_tfstate_key.Trim()
}
$changed = $true
}
}
if ($null -eq $sub -or "" -eq $sub) {
$sub = $tfstate_resource_id.Split("/")[2]
$iniContent[$combined]["STATE_SUBSCRIPTION"] = $sub.Trim()
$changed = $true
}
if ($null -eq $repo -or "" -eq $repo) {
$repo = Read-Host -Prompt "Please enter the path to the repo"
$iniContent["Common"]["repo"] = $repo.Trim()
$changed = $true
}
if ($changed) {
Out-IniFile -InputObject $iniContent -Path $filePath
}
$terraform_module_directory = Join-Path -Path $repo -ChildPath "\deploy\terraform\run\$Type"
$Env:TF_DATA_DIR = (Join-Path -Path $curDir -ChildPath ".terraform")
Write-Host -ForegroundColor green "Initializing Terraform"
if ($tfstate_resource_id.Length -gt 0) {
$sub = $tfstate_resource_id.Split("/")[2]
}
$Command = " init -upgrade=true -force-copy -backend-config ""subscription_id=$sub"" -backend-config ""resource_group_name=$rgName"" -backend-config ""storage_account_name=$saName"" -backend-config ""container_name=tfstate"" -backend-config ""key=$key"""
$bRunRefresh = $false
$deployment_parameter = " "
if (Test-Path ".terraform" -PathType Container) {
$jsonData = Get-Content -Path .\.terraform\terraform.tfstate | ConvertFrom-Json
if ("azurerm" -eq $jsonData.backend.type) {
$Command = " init -upgrade=true -force-copy -backend-config ""subscription_id=$sub"" -backend-config ""resource_group_name=$rgName"" -backend-config ""storage_account_name=$saName"" -backend-config ""container_name=tfstate"" -backend-config ""key=$key"""
if ($false -eq $Silent) {
$ans = Read-Host -Prompt "The system has already been deployed and the statefile is in Azure, do you want to redeploy Y/N?"
if ("Y" -ne $ans) {
$Env:TF_DATA_DIR = $null
return
}
$bRunRefresh = $true
}
}
}
else {
$deployment_parameter = " -var deployment=new "
}
$Cmd = "terraform -chdir=$terraform_module_directory $Command"
Add-Content -Path "deployment.log" -Value $Cmd
Write-Verbose $Cmd
& ([ScriptBlock]::Create($Cmd))
if ($LASTEXITCODE -ne 0) {
throw "Error executing command: $Cmd"
}
if ($Type -ne "sap_deployer") {
$tfstate_parameter = " -var tfstate_resource_id=" + $tfstate_resource_id
}
else {
# Removing the bootsrap shell script
if (Test-Path ".\post_deployment.sh" -PathType Leaf) {
Remove-Item -Path ".\post_deployment.sh" -Force
}
}
if ($Type -eq "sap_landscape") {
$tfstate_parameter = " -var tfstate_resource_id=" + $tfstate_resource_id
$deployer_tfstate_key_parameter = " -var deployer_tfstate_key=" + $deployer_tfstate_key
}
if ($Type -eq "sap_library") {
$tfstate_parameter = " -var tfstate_resource_id=" + $tfstate_resource_id
if ($false -eq $jsonData.deployer.use) {
$deployer_tfstate_key_parameter = ""
}
else {
if ($deployer_tfstate_key.Length -gt 0) {
$deployer_tfstate_key_parameter = " -var deployer_tfstate_key=" + $deployer_tfstate_key
}
else {
$deployer_tfstate_key_parameter = ""
}
}
}
if ($Type -eq "sap_system") {
$tfstate_parameter = " -var tfstate_resource_id=" + $tfstate_resource_id
if ($deployer_tfstate_key.Length -gt 0) {
$deployer_tfstate_key_parameter = " -var deployer_tfstate_key=" + $deployer_tfstate_key
}
else {
$deployer_tfstate_key_parameter = ""
}
$landscape_tfstate_key_parameter = " -var landscape_tfstate_key=" + $landscape_tfstate_key
}
if ($bRunRefresh) {
Write-Host -ForegroundColor green "Running refresh, please wait"
$Command = " refresh -var-file " + $ParamFullFile + $tfstate_parameter + $landscape_tfstate_key_parameter + $deployer_tfstate_key_parameter + $extra_vars + $version_parameter + $deployment_parameter
$Cmd = "terraform -chdir=$terraform_module_directory $Command"
Write-Verbose $Cmd
Add-Content -Path "deployment.log" -Value $Cmd
$planResultsPlain = & ([ScriptBlock]::Create($Cmd)) | Out-String
if ($LASTEXITCODE -ne 0) {
throw "Error executing command: $Cmd"
}
}
$Command = " output -no-color automation_version"
$Cmd = "terraform -chdir=$terraform_module_directory $Command"
Write-Verbose $Cmd
$versionLabel = & ([ScriptBlock]::Create($Cmd)) | Out-String
$version_parameter = " "
if ($versionLabel.Contains("Warning: No outputs found")) {
$deployment_parameter = " -var deployment=new "
}
else {
Write-Host $versionLabel
if ($versionLabel.Length -eq 0 ) {
Write-Host ""
Write-Host -ForegroundColor red "The environment was deployed using an older version of the Terrafrom templates"
Write-Host ""
Write-Host -ForegroundColor red "!!! Risk for Data loss !!!"
Write-Host ""
Write-Host -ForegroundColor red "Please inspect the output of Terraform plan carefully before proceeding"
Write-Host ""
if ($PSCmdlet.ShouldProcess($Parameterfile , $Type)) {
$ans = Read-Host -Prompt "Do you want to continue Y/N?"
if ("Y" -eq $ans) {
}
else {
$Env:TF_DATA_DIR = $null
return
}
}
}
else {
$version_parameter = " -var terraform_template_version=" + $versionLabel
Write-Host ""
Write-Host -ForegroundColor green "The environment was deployed using the $versionLabel version of the Terrafrom templates"
Write-Host ""
Write-Host ""
}
}
Write-Host -ForegroundColor green "Running plan, please wait"
if ($deployer_tfstate_key_parameter.Length -gt 0) {
$Command = " plan -no-color -var-file " + $ParamFullFile + $tfstate_parameter + $landscape_tfstate_key_parameter + $deployer_tfstate_key_parameter + $extra_vars + $version_parameter + $deployment_parameter
}
else {
$Command = " plan -no-color -var-file " + $ParamFullFile + $tfstate_parameter + $landscape_tfstate_key_parameter + $extra_vars + $version_parameter + $deployment_parameter
}
$Cmd = "terraform -chdir=$terraform_module_directory $Command"
Add-Content -Path "deployment.log" -Value $Cmd
Write-Verbose $Cmd
$planResultsPlain = & ([ScriptBlock]::Create($Cmd)) | Out-String
if ($LASTEXITCODE -ne 0) {
throw "Error executing command: $Cmd"
}
if ( $planResultsPlain.Contains('No changes')) {
Write-Host ""
Write-Host -ForegroundColor Green "Infrastructure is up to date"
Write-Host ""
$Env:TF_DATA_DIR = $null
return;
}
if ( $planResultsPlain.Contains('0 to add, 0 to change, 0 to destroy')) {
Write-Host ""
Write-Host -ForegroundColor Green "Infrastructure is up to date"
Write-Host ""
$Env:TF_DATA_DIR = $null
return;
}
Write-Host $planResults
if (-not $planResultsPlain.Contains('0 to change, 0 to destroy') ) {
Write-Host ""
Write-Host -ForegroundColor red "!!! Risk for Data loss !!!"
Write-Host ""
Write-Host -ForegroundColor red "Please inspect the output of Terraform plan carefully before proceeding"
Write-Host ""
if ($PSCmdlet.ShouldProcess($Parameterfile , $Type)) {
$ans = Read-Host -Prompt "Do you want to continue Y/N?"
if ("Y" -ne $ans) {
$Env:TF_DATA_DIR = $null
return
}
}
}
if ($PSCmdlet.ShouldProcess($Parameterfile , $Type)) {
Write-Host -ForegroundColor green "Running apply"
$Command = " apply " + $autoApprove + " -var-file " + $ParamFullFile + $tfstate_parameter + $landscape_tfstate_key_parameter + $deployer_tfstate_key_parameter + $extra_vars + $version_parameter + $deployment_parameter
$Cmd = "terraform -chdir=$terraform_module_directory $Command"
Add-Content -Path "deployment.log" -Value $Cmd
Write-Verbose $Cmd
& ([ScriptBlock]::Create($Cmd))
if ($LASTEXITCODE -ne 0) {
throw "Error executing command: $Cmd"
}
if ($Type -eq "sap_library") {
$Command = " output created_resource_group_name"
$Cmd = "terraform -chdir=$terraform_module_directory $Command"
$rgName = & ([ScriptBlock]::Create($Cmd)) | Out-String
if ($LASTEXITCODE -ne 0) {
throw "Error executing command: $Cmd"
}
$iniContent[$combined]["REMOTE_STATE_RG"] = $rgName.Replace("""", "")
$Command = " output remote_state_storage_account_name"
$Cmd = "terraform -chdir=$terraform_module_directory $Command"
$saName = & ([ScriptBlock]::Create($Cmd)) | Out-String
if ($LASTEXITCODE -ne 0) {
throw "Error executing command: $Cmd"
}
$iniContent[$combined]["REMOTE_STATE_SA"] = $saName.Replace("""", "")
$Command = " output tfstate_resource_id"
$Cmd = "terraform -chdir=$terraform_module_directory $Command"
$tfstate_resource_id = & ([ScriptBlock]::Create($Cmd)) | Out-String
if ($LASTEXITCODE -ne 0) {
throw "Error executing command: $Cmd"
}
$iniContent[$combined]["tfstate_resource_id"] = $tfstate_resource_id
Out-IniFile -InputObject $iniContent -Path $filePath
}
}
$Env:TF_DATA_DIR = $null
}