зеркало из
1
0
Форкнуть 0

add azure SDK sample setup script

This commit is contained in:
Wei Shi 2021-05-26 19:41:23 -07:00
Родитель e414f3b88d
Коммит 54a5223aff
2 изменённых файлов: 729 добавлений и 0 удалений

564
tools/Common.psm1 Normal file
Просмотреть файл

@ -0,0 +1,564 @@
# Continuously evaluates an expression ($CheckExpression) until it becomes false.
# If $CheckExpression tries to obtain a resource, then the function returns that resource after timeout, or whatever the evaluation returns when the resource is no longer obtainable, such as $null.
# The function will continue to loop also if there was an exception invoking the $CheckExpression until it has waited $MaxSleepSeconds between checks.
function Wait-IncrementsUntilTimeOut
{
Param
(
# The string of the condition to check until it becomes false.
[Parameter(Mandatory = $true)]
[string] $CheckExpression,
# The maximum seconds to wait before timeout.
[int] $MaxSleepSeconds=300,
# The amount of seconds to sleep between each evaulation of the $CheckExpression.
[int] $SleepSecondsUnit=5,
# Negates the loop logic based on $CheckExpression, unless there is an exception evaluating $CheckExpression.
# This is preferable to adding "!" or "-not" where you lose evaluated information by reducing returned information to $true or $false.
[switch] $Negate
)
$currentSeconds = 0
$expressionResult = $null
# $currentSeconds should not be compared to a multiple of $sleepSecondUnit since it won't tell us if the operation was successful because it could be that
# it was successful at the last second. If the $currentSeconds is ($maxSleepSeconds+1) or over, we know for sure the operation took too long.
while ($currentSeconds -lt ($MaxSleepSeconds + 1))
{
# If $expressionResult is $null or $false, then the loop stops. If $expressionResult is non-empty, then the loop continues. Returns $expressionResult.
try
{
$expressionResult = Invoke-Expression $CheckExpression
$stringOutput = $expressionResult
$loop = $expressionResult
if ($Negate)
{
$loop = !$expressionResult
}
}
catch
{
Write-Verbose -Message "ERROR running command: ${CheckExpression}" -Verbose
Write-Verbose -Message $_.ScriptStackTrace -Verbose
Write-Verbose -Message "$($_ | Out-String)" -Verbose
$loop = $true
}
$invalidToString = $false
try
{
$stringOutput = $expressionResult.toString()
}
catch
{
$invalidToString = $true
$stringOutput = $expressionResult | Out-String
Write-Verbose -Message "'${CheckExpression}' does not have a valid toString(), and has an Out-String of '${stringOutput}'." -Verbose
}
if (!$invalidToString)
{
Write-Verbose -Message "'${CheckExpression}' evaluated to '${stringOutput}'." -Verbose
}
# $loop is $true when $CheckExpression evaluates to truthy value, but if $Negate is true, then that logic is reversed
# $loop is also $true if $CheckExpression evaluation throws an exception.
if ($loop)
{
Write-Verbose -Message "Waited for ${currentSeconds} seconds. Waiting ${SleepSecondsUnit} more seconds and then running again..." -Verbose
$currentSeconds += $SleepSecondsUnit
Start-Sleep -Seconds $SleepSecondsUnit
}
else
{
break
}
}
if ($currentSeconds -ge ($MaxSleepSeconds + 1))
{
Write-Verbose -Message "'${CheckExpression}' still evaluates to '${stringOutput}' after timeout of ${MaxSleepSeconds} seconds." -Verbose
}
else
{
Write-Verbose -Message "'${CheckExpression}' evaluated to a value of '${stringOutput}' before timeout of ${MaxSleepSeconds} seconds." -Verbose
}
$expressionResult
}
# Creates a new service principal client Id and secret. Only required if a service principal does not already exist. Requires admin privileges to create.
function New-AzureAppSp
{
[CmdletBinding(DefaultParameterSetName = "notAdfs")]
param
(
[Parameter(ParameterSetName = "Aad", Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[string] $ServicePrincipalName,
[Parameter(ParameterSetName = "Adfs", Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[string] $ApplicationName,
[Parameter(ParameterSetName = "Aad", Mandatory = $false)]
[Parameter(ParameterSetName = "Adfs", Mandatory = $false)]
[ValidateNotNullOrEmpty()]
[string] $ErcsVmDnsName,
[Parameter(ParameterSetName = "Adfs", Mandatory = $true)]
[System.Management.Automation.PSCredential] $AzureAdminCredential
)
if (!$ErcsVmDnsName)
{
. "C:\CloudDeployment\BVTs\BVTSettingUtils.ps1"
$ErcsVmDnsName = Get-Setting -key "AZStack\ercsVM"
}
$servicePrincipal = $null
$loginInfo = @{
"ClientId" = $null;
"ClientSecret" = $null;
"ObjectId" = $null
}
if ($ApplicationName)
{
Write-Verbose -Message "Creating Service Principal ${ApplicationName}." -Verbose
$ercsVmName = $ErcsVmDnsName.Split(".")[0]
$session = New-PSSession -ComputerName $ercsVmName -ConfigurationName PrivilegedEndpoint -Credential $AzureAdminCredential
$applications = Invoke-Command -Session $session -ScriptBlock { Get-GraphApplication }
$servicePrincipalApplication = $applications | Where-Object {$_.Name -like "*${ApplicationName}*"}
if ($servicePrincipalApplication)
{
foreach ($application in $servicePrincipalApplication)
{
Invoke-Command -Session $session -ScriptBlock { Remove-GraphApplication -ApplicationIdentifier $using:application.Identifier}
}
Get-AzADApplication -DisplayNameStartWith "Azurestack-${ApplicationName}" -ErrorAction SilentlyContinue | Remove-AzADApplication -Force
}
$servicePrincipal = Invoke-Command -Session $session -ScriptBlock { New-GraphApplication -Name $using:ApplicationName -GenerateClientSecret}
$spExistsExpression = "Invoke-Command -Session (Get-PSSession -Id $($session.Id)) -ScriptBlock { Get-GraphApplication -ApplicationIdentifier $($servicePrincipal.ApplicationIdentifier)}"
$spExists = Wait-IncrementsUntilTimeOut -CheckExpression $spExistsExpression -Negate
if (!$spExists)
{
throw "The service principal $($servicePrincipal.Id) still failed to exist after checking for an alloted amount of time."
}
Write-Verbose -Message "Created the following service principal:" -Verbose
$servicePrincipal | Out-String | Write-Verbose -Verbose
$roleAssignmentExpression = "(New-AzRoleAssignment -RoleDefinitionName Owner -ApplicationId $($servicePrincipal.ClientId))"
$role = Wait-IncrementsUntilTimeOut -CheckExpression $roleAssignmentExpression -Negate
if (!$role)
{
throw "Failed to assign role Owner to service principal $($servicePrincipal.ClientId)"
}
$loginInfo["ClientId"] = $servicePrincipal.ClientId
# The client secret in ADFS is in plain text, no need to convert from SecureString for now. Reconsider this in the future if this changes.
$loginInfo["ClientSecret"] = $servicePrincipal.ClientSecret | ConvertTo-SecureString -AsPlainText -Force | ConvertFrom-SecureString
$loginInfo["ObjectId"] = $servicePrincipal.ApplicationIdentifier
# Clean up and reset context.
$session | Remove-PSSession
}
else
{
Write-Verbose -Message "Creating Service Principal ${ServicePrincipalName}." -Verbose
$servicePrincipal = Get-AzADServicePrincipal -DisplayName $ServicePrincipalName
if ($servicePrincipal)
{
Remove-AzADServicePrincipal -ObjectId $servicePrincipal.Id -Force
}
$servicePrincipalApplication = Get-AzADApplication -DisplayName $ServicePrincipalName
if ($servicePrincipalApplication)
{
$servicePrincipalApplication | Remove-AzADApplication -Force
}
$servicePrincipal = New-AzADServicePrincipal -Role Owner -DisplayName $ServicePrincipalName
$spExistsExpression = "((Get-AzADServicePrincipal -ObjectId $($servicePrincipal.Id)) -and (Get-AzADApplication -DisplayName ${ServicePrincipalName}))"
$spExists = Wait-IncrementsUntilTimeOut -CheckExpression $spExistsExpression -Negate
if (!$spExists)
{
throw "The service principal $($servicePrincipal.Id) still failed to exist after checking for an alloted amount of time."
}
Write-Verbose -Message "Created the following service principal:" -Verbose
$servicePrincipal | Out-String | Write-Verbose -Verbose
$loginInfo["ClientId"] = $servicePrincipal.ApplicationId
$loginInfo["ClientSecret"] = ConvertFrom-SecureString $servicePrincipal.Secret
$loginInfo["ObjectId"] = $servicePrincipal.Id
}
if (-not $servicePrincipal)
{
throw [System.Exception] "The service principal is null. Failed to create and connect to a service principal context named ${ServicePrincipalName}."
}
return $loginInfo
}
function New-AzureCertSp
{
[CmdletBinding(DefaultParameterSetName = "notAdfs")]
param
(
[Parameter(ParameterSetName = "notAdfs", Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[string] $ServicePrincipalName,
[Parameter(ParameterSetName = "Adfs", Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[string] $ApplicationName,
[Parameter(ParameterSetName = "notAdfs", Mandatory = $false)]
[Parameter(ParameterSetName = "Adfs", Mandatory = $false)]
[ValidateNotNullOrEmpty()]
[string] $ErcsVmDnsName,
[Parameter(ParameterSetName = "notAdfs", Mandatory = $true)]
[Parameter(ParameterSetName = "Adfs", Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[string] $CertificateName,
[Parameter(ParameterSetName = "Adfs", Mandatory = $true)]
[System.Management.Automation.PSCredential] $AzureAdminCredential,
[Parameter(ParameterSetName = "Adfs", Mandatory = $false)]
[Parameter(ParameterSetName = "notAdfs", Mandatory = $false)]
[ValidateNotNullOrEmpty()]
[string] $CertPfxOutputPath,
[Parameter(ParameterSetName = "Adfs", Mandatory = $false)]
[Parameter(ParameterSetName = "notAdfs", Mandatory = $false)]
[ValidateNotNullOrEmpty()]
[System.Security.SecureString] $CertPfxPassword
)
if (!$ErcsVmDnsName)
{
. "C:\CloudDeployment\BVTs\BVTSettingUtils.ps1"
$ErcsVmDnsName = Get-Setting -key "AZStack\ercsVM"
}
$certPath = "cert:\LocalMachine\My"
Get-ChildItem -Path $certPath | Where-Object Subject -Match $CertificateName | Remove-Item -Force
$subjectName = "CN=" + $CertificateName
$certificate = New-SelfSignedCertificate -CertStoreLocation $certPath -Subject $subjectName -KeySpec KeyExchange
$keyValue = [System.Convert]::ToBase64String($certificate.GetRawCertData())
$servicePrincipal = $null
$loginInfo = @{
"ClientId" = $null;
"CertificateThumbprint" = $certificate.Thumbprint;
"ObjectId" = $null
}
if ($ApplicationName)
{
Write-Verbose -Message "Creating Service Principal ${ApplicationName}." -Verbose
$ercsVmName = $ErcsVmDnsName.Split(".")[0]
$session = New-PSSession -ComputerName $ercsVmName -ConfigurationName PrivilegedEndpoint -Credential $AzureAdminCredential
$applications = Invoke-Command -Session $session -ScriptBlock { Get-GraphApplication }
$servicePrincipalApplication = $applications | Where-Object {$_.Name -like "*${ApplicationName}*"}
if ($servicePrincipalApplication)
{
foreach ($application in $servicePrincipalApplication)
{
Invoke-Command -Session $session -ScriptBlock { Remove-GraphApplication -ApplicationIdentifier $using:application.Identifier }
}
Get-AzADApplication -DisplayNameStartWith "Azurestack-${ApplicationName}" -ErrorAction SilentlyContinue | Remove-AzADApplication -Force
}
$servicePrincipal = Invoke-Command -Session $session -ScriptBlock { New-GraphApplication -Name $using:ApplicationName -ClientCertificates $using:certificate}
$spExistsExpression = "Invoke-Command -Session (Get-PSSession -Id $($session.Id)) -ScriptBlock { Get-GraphApplication -ApplicationIdentifier $($servicePrincipal.ApplicationIdentifier)}"
$spExists = Wait-IncrementsUntilTimeOut -CheckExpression $spExistsExpression -Negate
if (!$spExists)
{
throw "The service principal $($servicePrincipal.Id) still failed to exist after checking for an alloted amount of time."
}
Write-Verbose -Message "Created the following service principal:" -Verbose
$servicePrincipal | Out-String | Write-Verbose -Verbose
$roleAssignmentExpression = "(New-AzRoleAssignment -RoleDefinitionName Owner -ApplicationId $($servicePrincipal.ClientId))"
$role = Wait-IncrementsUntilTimeOut -CheckExpression $roleAssignmentExpression -Negate
if (!$role)
{
throw "Failed to assign role Owner to service principal $($servicePrincipal.ClientId)"
}
# The ADFS applicationId is in the ClientId property for some reason.
$loginInfo["ClientId"] = $servicePrincipal.ClientId
$loginInfo["ObjectId"] = $servicePrincipal.ApplicationIdentifier
# Clean up and reset context.
$session | Remove-PSSession
}
else
{
Write-Verbose -Message "Creating Service Principal ${ServicePrincipalName}." -Verbose
$servicePrincipal = Get-AzADServicePrincipal -DisplayName $ServicePrincipalName
if ($servicePrincipal)
{
Remove-AzADServicePrincipal -ObjectId $servicePrincipal.Id -Force
}
$servicePrincipalApplication = Get-AzADApplication -DisplayName $ServicePrincipalName
if ($servicePrincipalApplication)
{
$servicePrincipalApplication | Remove-AzADApplication -Force
}
$servicePrincipal = New-AzADServicePrincipal -DisplayName $ServicePrincipalName -CertValue $keyValue -EndDate $certificate.NotAfter -StartDate $certificate.NotBefore
$spExistsExpression = "((Get-AzADServicePrincipal -ObjectId $($servicePrincipal.Id)) -and (Get-AzADApplication -DisplayName ${ServicePrincipalName}))"
$spExists = Wait-IncrementsUntilTimeOut -CheckExpression $spExistsExpression -Negate
if (!$spExists)
{
throw "The service principal $($servicePrincipal.Id) still failed to exist after checking for an alloted amount of time."
}
Write-Verbose -Message "Created the following service principal:" -Verbose
$servicePrincipal | Out-String | Write-Verbose -Verbose
$roleAssignmentExpression = "(New-AzRoleAssignment -RoleDefinitionName Owner -ApplicationId $($servicePrincipal.ApplicationId))"
$role = Wait-IncrementsUntilTimeOut -CheckExpression $roleAssignmentExpression -Negate
if (!$role)
{
throw "Failed to assign role Owner to service principal $($servicePrincipal.ApplicationId)"
}
$loginInfo["ClientId"] = $servicePrincipal.ApplicationId
$loginInfo["ObjectId"] = $servicePrincipal.Id
}
if (-not $servicePrincipal)
{
throw [System.Exception] "The service principal is null. Failed to create and connect to a service principal context named ${ServicePrincipalName}."
}
if (($CertPfxOutputPath -and !$CertPfxPassword) -or (!$CertPfxOutputPath -and $CertPfxPassword))
{
throw [System.Exception] "ERROR: Both the `$CertPfxOutput and `$CertPfxPassword parameters are required or both should not be passed."
}
elseif ($CertPfxOutputPath)
{
Get-ChildItem -Path $certPath | Where-Object {$_.Subject -eq "CN=${CertificateName}"} | Export-PfxCertificate -FilePath $CertPfxOutputPath -Password $CertPfxPassword | Out-Null
}
return $loginInfo
}
function Install-SoftwareFromURL
{
param
(
[Parameter(Mandatory = $true)]
[string] $DownloadURL,
# Specify location to install files, currently only used for .exe files.
[Parameter(Mandatory = $false)]
[string] $InstallDirectory,
# Used to name the downloaded file before installation. Must include extension. The last part of URL used as file name and extension by default.
[Parameter(Mandatory = $false)]
[string] $DownloadedFileName,
# The likeness string is used to check if the software is already installed. This is not required.
[Parameter(Mandatory = $false)]
[string] $RegistryDisplayNameLike,
[Parameter(Mandatory = $false)]
[string] $InstallLogFilePath,
# Force install doesn't work for msi files since windows will throw 1603 exit code error if the software already exists.
[Parameter(Mandatory = $false)]
[switch] $ForceInstall
)
if ($DownloadedFileName -and $DownloadedFileName.Split(".").Count -lt 2 )
{
throw "Error: `$DownloadedFileName must include an extension!"
}
$install = $true
if (!$ForceInstall -and $RegistryDisplayNameLike)
{
$x86_check = ((Get-ItemProperty "HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*") | where-object { $_.DisplayName -like $RegistryDisplayNameLike } ).DisplayName.Length -gt 0;
if (Test-Path 'HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall') {
$x64_check = ((Get-ItemProperty "HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*") | where-object { $_.DisplayName -like $RegistryDisplayNameLike } ).DisplayName.Length -gt 0;
}
if ($x64_check -or $x86_check) {
$install = $false
Write-Verbose -Message "The software from ${DownloadURL} was already installed." -Verbose
}
}
if ($install)
{
if ($ForceInstall)
{
Write-Verbose -Message "WARNING: ForceInstall option selected. Might not succeed if software with higher version already installed or other reasons." -Verbose
}
Write-Verbose -Message "Downloading: ${DownloadURL}" -Verbose
if (!($DownloadedFileName))
{
$fileName = $DownloadURL.Split("/")[-1]
$extension = $fileName.Split(".")[-1]
}
else
{
$fileName = $DownloadedFileName
$extension = $DownloadedFileName.Split(".")[-1]
}
$tempDirObject = New-Item -Path ([System.IO.Path]::GetTempPath()) -Name ([System.IO.Path]::GetRandomFileName()) -ItemType "directory"
$tempDir = $tempDirObject.FullName
$downloadedPath = Join-Path -Path $tempDir -ChildPath $fileName
$sleepSecondsUnit = 5
$maxSleepSeconds = 60
$currentSeconds = 0
# $currentSeconds should not be compared to a multiple of $sleepSecondUnit since it won't tell us if the operation was successful because it could be that
# it was successful at the last second. If the $currentSeconds is ($maxSleepSeconds+1) or over, we know for sure the operation took too long.
while ($currentSeconds -lt $maxSleepSeconds + 1)
{
Write-Verbose -Message "Attempting to download ${DownloadURL}, ignore errors until timeout. $($maxSleepSeconds - $currentSeconds) seconds until timeout error." -Verbose
try
{
$webClient = New-Object System.Net.WebClient
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$webClient.DownloadFile($DownloadURL, $downloadedPath)
Write-Verbose -Message "Finished downloading ${DownloadURL}." -Verbose
break
}
catch
{
Write-Verbose $_
$currentSeconds += $sleepSecondsUnit
Start-Sleep -Seconds $sleepSecondsUnit
Write-Verbose -Message "Failed to download file, retrying..." -Verbose
}
}
if ($currentSeconds -ge $maxSleepSeconds + 1)
{
Remove-Item $tempDir -Recurse
throw [System.Exception] "Attempts to download ${DownloadURL} command timed out."
}
Write-Verbose -Message "Performing quiet install" -Verbose
####################
# INSTALL EXE
####################
if ($extension -eq "exe")
{
$arguments = "/SP- /VERYSILENT /SUPPRESSMSGBOXES /FORCECLOSEAPPLICATIONS InstallAllUsers=1 PrependPath=1"
if ($InstallDirectory)
{
$arguments += " TargetDir=`"${InstallDirectory}`""
}
if ($InstallLogFilePath)
{
$arguments += " /log=${InstallLogFilePath}"
}
$runExpression = "(Start-Process -FilePath '${downloadedPath}' -ArgumentList '${arguments}' -Wait -PassThru -NoNewWindow).ExitCode -ne 0"
$installFailed = Wait-IncrementsUntilTimeOut -CheckExpression $runExpression -SleepSecondsUnit 20 -MaxSleepSeconds 600
}
####################
# INSTALL MSI
####################
elseif ($extension -eq "msi") {
$arguments = "/i ${downloadedPath} /quiet AllUsers=1"
if ($InstallLogFilePath)
{
$arguments += " /L*V ${InstallLogFilePath}"
}
$runExpression = "(Start-Process msiexec -ArgumentList '${arguments}' -Wait -PassThru -NoNewWindow).ExitCode -ne 0"
$installFailed = Wait-IncrementsUntilTimeOut -CheckExpression $runExpression -SleepSecondsUnit 20 -MaxSleepSeconds 600
}
if ($installFailed)
{
Remove-Item $tempDir -Recurse
Write-Verbose -Message "Last error message: $Error[0]" -Verbose
throw "Failed to install: $DownloadURL"
}
Write-Verbose -Message "Successfully installed: $DownloadURL" -Verbose
Remove-Item $tempDir -Recurse
}
else
{
Write-Verbose -Message "Skipping installation of software from ${DownloadURL}." -Verbose
}
}
function Import-AzModules
{
param
(
[Parameter(Mandatory = $true)]
[string] $AzVersion
)
Import-Module -Global "$($env:SystemDrive)\az.${AzVersion}\Az.Accounts" -Verbose:$false -ErrorAction Stop
Import-Module -Global "$($env:SystemDrive)\az.${AzVersion}\Az.Resources" -Verbose:$false -ErrorAction Stop
$modules = Get-ChildItem -Path "$($env:SystemDrive)\az.${AzVersion}" -Filter "*.psd1" -Recurse `
| Where-Object { $_.Name -ne "Az.psd1" -and $_.Name -ne "AzureStack.psd1" -and $_.Name -ne "Az.Accounts.psd1" -and $_.Name -ne "Az.Resources.psd1" } `
| ForEach-Object { $_.FullName }
foreach ($module in $modules)
{
Import-Module -Global $module -Verbose:$false -ErrorAction Stop
}
}
# Creates an Azure Stack self-signed .pem file for NodeJS environment variable NODE_EXTRA_CA_CERTS that will be use by NodeJS application.
function New-NodeJSEnvPem
{
param
(
[Parameter(Mandatory = $true)]
# The folder to contain the final .pem file.
[string] $PemFolder
)
$certName = "AzureStackSelfSignedRootCert"
$rootcerts = Get-ChildItem Cert:\LocalMachine\my | Where-Object Subject -eq "CN=${certName}"
if (-not $rootcerts) {
Write-Host "Cerficate with subject CN=${certName} not found"
throw "Cerficate with subject CN=${certName} not found"
}
$pemPaths = New-Object System.Collections.ArrayList
for ($i = 0; $i -lt $rootcerts.Count; $i++)
{
Write-Host "Exporting certificate $($rootcerts[$i].Subject)"
$exportRootCert = "$($rootcerts[$i].SubjectName.Name.split("=")[1])$($rootcerts[$i].SerialNumber)${i}"
$certFile = [System.IO.Path]::Combine($PemFolder,"${exportRootCert}.cer")
Export-Certificate -Type CERT -FilePath $certFile -Cert $rootcerts[$i]
$opensslExe = [System.IO.Path]::Combine($env:ProgramFiles, "Git", "usr", "bin", "openssl.exe")
$pemFile = [System.IO.Path]::Combine($PemFolder, "${exportRootCert}.pem")
& $opensslExe x509 -inform der `
-in $certFile `
-out $pemFile
$pemPaths.Add($pemFile)
Remove-Item -Path $certFile -Force
}
$bundledPem = [System.IO.Path]::Combine($PemFolder,"AzureStackSelfSignedBundlePem.pem")
New-Item -Path $bundledPem -ItemType "file"
foreach ($pem in $pemPaths)
{
Get-Content -Path $pem | Add-Content -Path $bundledPem
}
[System.Environment]::SetEnvironmentVariable('NODE_EXTRA_CA_CERTS', $bundledPem, [System.EnvironmentVariableTarget]::Machine)
Write-Host "Added ${bundledPem} file to NodeJS environment variable NODE_EXTRA_CA_CERTS."
}

Просмотреть файл

@ -0,0 +1,165 @@
param
(
[Parameter(Mandatory = $true)]
[string] $AzureSubscriptionId,
# Create a password to protect the exported certificate .pfx file.
[Parameter(Mandatory = $true)]
[System.Security.SecureString] $SecureCertPfxPassword,
[Parameter(Mandatory = $false)]
[switch] $NodeJS,
[Parameter(Mandatory = $false)]
[switch] $Golang,
# This variable is used to load existing service principal details from another powershell script. The dot notation will be used to load that script. For the existing details,
# the .ps1 file needs to contain two hashtables for the service principal application and certificate hashtable with the following keys accessible from this script:
# $azureAppSpInfo["ClientId"]
# $azureAppSpInfo["ClientSecret"]
# $azureAppSpInfo["ObjectId"]
# $azureCertSpInfo["ClientId"]
# $azureCertSpInfo["CertificateThumbprint"]
# $azureCertSpInfo["ObjectId"]
[Parameter(Mandatory = $false)]
[string] $ExistingServicePrincipalDetails
)
. "C:\CloudDeployment\BVTs\BVTSettingUtils.ps1"
Import-Module $([io.path]::combine($PSScriptRoot, "Common.psm1"))
Import-AzModules -AzVersion "0.10.0"
# Retrieve BVTSettings.xml settings.
$adminName = Get-Setting -key ServiceAdminUpn
$adminPassSecureString = ConvertTo-SecureString -String (Get-Setting -key ServiceAdminPassword) -AsPlainText -Force
$deploymentId = Get-Setting -key DeploymentId
$adfs = ([System.String](Get-Setting -key AadLoginUri)).EndsWith("/adfs")
$azureTenantId = Get-Setting -key AADTenantID
$azureLocation = Get-Setting -Key AzureStackLocation
$azureAdminArmEndpoint = Get-Setting -key ARMEndpoint
# Log into Azure admin context
Add-AzEnvironment -Name AzureSampleEnvironment -ARMEndpoint $azureAdminArmEndpoint
$username = $adminName
$password = $adminPassSecureString
$credential = [System.Management.Automation.PSCredential]::new($username, $password)
Connect-AzAccount -Environment AzureSampleEnvironment -Credential $credential -Tenant $azureTenantId
# Undocumented 16 character limit for application names for creating service principals.
$uniqueId = ($deploymentId -replace "-", "").Substring(0,6)
$azureAppSpName = "sampappsp${uniqueId}"
$azureCertSpName = "sampcertsp${uniqueId}"
$certName = "azureSampleCert"
$certPfxDir = [io.path]::combine($env:HOMEPATH, "cert")
$certPfxPath = [io.path]::combine($certPfxDir, "azSampleCert.pfx")
if (!(Test-Path $certPfxDir))
{
New-Item -ItemType "directory" -Path $certPfxDir -Force
}
if (!$ExistingServicePrincipalDetails)
{
if ($adfs)
{
$azureAppSpInfo = New-AzureAppSp -ApplicationName $azureAppSpName -AzureAdminCredential $credential
$azureCertSpInfo = New-AzureCertSp -ApplicationName $azureCertSpName `
-CertificateName $certName `
-AzureAdminCredential $credential `
-CertPfxOutputPath $certPfxPath `
-CertPfxPassword $SecureCertPfxPassword
}
else
{
$azureAppSpInfo = New-AzureAppSp -ServicePrincipalName $azureAppSpName
$azureCertSpInfo = New-AzureCertSp -ServicePrincipalName $azureCertSpName `
-CertificateName $certName `
-CertPfxOutputPath $certPfxPath `
-CertPfxPassword $SecureCertPfxPassword
}
}
else
{
. $ExistingServicePrincipalDetails
}
$appSpSecret = ConvertTo-SecureString $azureAppSpInfo["ClientSecret"]
$appSpBSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($appSpSecret)
$certPassBSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($SecureCertPfxPassword)
$certPfxPassword = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($certPassBSTR)
# Warning: the following code does not work on unix systems as of 5/28/2021 because [System.Environment]::SetEnvironmentVariable is unimplemented for unix.
[System.Environment]::SetEnvironmentVariable('AZURE_TENANT_ID', $azureTenantId, [System.EnvironmentVariableTarget]::Machine)
# Service principal application details
[System.Environment]::SetEnvironmentVariable('AZURE_SP_APP_ID', $azureAppSpInfo["ClientId"], [System.EnvironmentVariableTarget]::Machine)
[System.Environment]::SetEnvironmentVariable('AZURE_SP_APP_SECRET', [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($appSpBSTR), [System.EnvironmentVariableTarget]::Machine)
[System.Environment]::SetEnvironmentVariable('AZURE_SP_APP_OBJECT_ID', $azureAppSpInfo["ObjectId"], [System.EnvironmentVariableTarget]::Machine)
# Service principal certificate details
[System.Environment]::SetEnvironmentVariable('AZURE_SP_CERT_ID', $azureCertSpInfo["ClientId"], [System.EnvironmentVariableTarget]::Machine)
[System.Environment]::SetEnvironmentVariable('AZURE_SP_CERT_THUMBPRINT', $azureCertSpInfo["CertificateThumbprint"], [System.EnvironmentVariableTarget]::Machine)
[System.Environment]::SetEnvironmentVariable('AZURE_SP_CERT_OBJECT_ID', $azureCertSpInfo["ObjectId"], [System.EnvironmentVariableTarget]::Machine)
[System.Environment]::SetEnvironmentVariable('AZURE_SP_CERT_PATH', $certPfxPath, [System.EnvironmentVariableTarget]::Machine)
[System.Environment]::SetEnvironmentVariable('AZURE_SP_CERT_PASS', $certPfxPassword, [System.EnvironmentVariableTarget]::Machine)
[System.Environment]::SetEnvironmentVariable('AZURE_SUBSCRIPTION_ID', $AzureSubscriptionId, [System.EnvironmentVariableTarget]::Machine)
[System.Environment]::SetEnvironmentVariable('AZURE_ARM_ENDPOINT', $azureAdminArmEndpoint, [System.EnvironmentVariableTarget]::Machine)
[System.Environment]::SetEnvironmentVariable('AZURE_LOCATION', $azureLocation, [System.EnvironmentVariableTarget]::Machine)
######################
# SDK SPECIFIC SETUP
######################
switch ($env:PROCESSOR_ARCHITECTURE)
{
"AMD64" { $architecture = "x64" }
"x86" { $architecture = "x86" }
default { throw "PowerShell package for OS architecture '$_' is not supported." }
}
$logFolder = $([io.path]::combine($env:HOMEPATH, "AzureSampleLogs"))
If (-not (Test-Path -Path $logFolder))
{
New-Item $logFolder -Type Directory -ErrorAction Stop
}
if ($NodeJS)
{
# Install NodeJS
$nodeJSVersion = "v14.17.0"
$nodeJSDownloadURL = "https://nodejs.org/dist/${nodeJSVersion}/node-${nodeJSVersion}-${architecture}.msi"
$nodeJSInstallLogFilePath = Join-Path -Path $logFolder -ChildPath "nodejsInstall.log"
Install-SoftwareFromURL -DownloadURL $nodeJSDownloadURL `
-RegistryDisplayNameLike "Node.js" `
-InstallLogFilePath $nodeJSInstallLogFilePath `
-ForceInstall
# Set up Azure Stack local certificate for NodeJS.
$certFolder = [System.IO.Path]::Combine($env:HOMEPATH, "certs")
if (!(Test-Path -Path $certFolder))
{
New-Item -ItemType "directory" -Path $certFolder
}
New-NodeJSEnvPem -PemFolder $certFolder
}
if ($Golang)
{
# Install Golang
# To get the latest Golang version use:
# $wc = New-Object System.Net.WebClient
# [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
# $latestVersion = $wc.DownloadString("https://golang.org/VERSION?m=text")
# WARNING: Golang updates usually have breaking changes, becareful when updating.
$golangVersion = "go1.15.8"
$downloadFileName = "${golangVersion}.windows-amd64.msi"
$golangDownloadURL = "https://golang.org/dl/${downloadFileName}"
$GoSDKInstallLogFilePath = Join-Path -Path $logFolder -ChildPath "goInstall.log"
Install-SoftwareFromURL -DownloadURL $golangDownloadURL `
-RegistryDisplayNameLike "Go Programming Language*" `
-InstallLogFilePath $GoSDKInstallLogFilePath `
-ForceInstall
}