Skip to content

Commit

Permalink
DSOS-2601: powershell fixes for win2012 (#556)
Browse files Browse the repository at this point in the history
* support pwsh 4

* fix

* comments

* Fix

* add user-data script

* fix

* test

* fix

* fix

* test

* test

* Fix

* fix

* fix

* fix

* Fixes

* fix

* Readme fix

* fix

* readme

* fix

* bump timeout

* Fix

* fix

* more debug

* fix
  • Loading branch information
drobinson-moj authored Feb 13, 2024
1 parent 5749119 commit eb592c2
Show file tree
Hide file tree
Showing 9 changed files with 126 additions and 50 deletions.
6 changes: 3 additions & 3 deletions powershell/Modules/ModPlatformAD/ModPlatformAD.psd1
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
#
# Generated by: Ministry of Justice
#
# Generated on: 09/02/2024
# Generated on: 12/02/2024
#

@{
Expand All @@ -12,7 +12,7 @@
# RootModule = ''

# Version number of this module.
ModuleVersion = '1.0.0.1'
ModuleVersion = '1.0.0.2'

# Supported PSEditions
# CompatiblePSEditions = @()
Expand All @@ -33,7 +33,7 @@ Copyright = '(c) 2024 Crown Copyright (Ministry of Justice)'
Description = 'Modernisation Platform ModPlatformAD module'

# Minimum version of the PowerShell engine required by this module
PowerShellVersion = '5.1.0.0'
PowerShellVersion = '4.0'

# Name of the PowerShell host required by this module
# PowerShellHostName = ''
Expand Down
4 changes: 2 additions & 2 deletions powershell/Modules/ModPlatformAD/ModPlatformADComputer.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ function Rename-ModPlatformADComputer {

$ErrorActionPreference = "Stop"

$Token = Invoke-RestMethod -TimeoutSec 2 -Headers @{"X-aws-ec2-metadata-token-ttl-seconds"=3600} -Method PUT -Uri http://169.254.169.254/latest/api/token
$InstanceId = Invoke-RestMethod -TimeoutSec 2 -Headers @{"X-aws-ec2-metadata-token" = $Token} -Method GET -Uri http://169.254.169.254/latest/meta-data/instance-id
$Token = Invoke-RestMethod -TimeoutSec 10 -Headers @{"X-aws-ec2-metadata-token-ttl-seconds"=3600} -Method PUT -Uri http://169.254.169.254/latest/api/token
$InstanceId = Invoke-RestMethod -TimeoutSec 10 -Headers @{"X-aws-ec2-metadata-token" = $Token} -Method GET -Uri http://169.254.169.254/latest/meta-data/instance-id
$TagsRaw = aws ec2 describe-tags --filters "Name=resource-id,Values=$InstanceId"
$Tags = "$TagsRaw" | ConvertFrom-Json

Expand Down
69 changes: 51 additions & 18 deletions powershell/Modules/ModPlatformAD/ModPlatformADConfig.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ function Get-ModPlatformADConfig {
Retrieve appropriate AD config for the given Modernisation Platform environment.
.DESCRIPTION
Either pass in the doman name as a parameter, or derive the AD configuration
from EC2 tags (environment-name or domain-name).
Either pass in the domain name as a parameter, or derive the AD configuration
from EC2 tags (environment-name or domain-name).
EC2 requires permissions to get tags and the aws cli.
.PARAMETER DomainNameFQDN
Expand Down Expand Up @@ -38,7 +38,6 @@ function Get-ModPlatformADConfig {
)
"SecretAccountName" = "hmpps-domain-services-test"
"SecretName" = "/microsoft/AD/azure.noms.root/shared-passwords"
"SecretRoleName" = "EC2HmppsDomainSecretsRole"
"DomainNameFQDN" = "azure.noms.root"
"DomainNameNetbios" = "AZURE"
"DomainJoinUsername" = "svc_join_domain"
Expand All @@ -51,37 +50,71 @@ function Get-ModPlatformADConfig {
"planetfm-production",
"corporate-staff-rostering-preproduction",
"corporate-staff-rostering-production"
)
)
"SecretAccountName" = "hmpps-domain-services-production"
"SecretName" = "/microsoft/AD/azure.hmpp.root/shared-passwords"
"SecretRoleName" = "EC2HmppsDomainSecretsRole"
"DomainNameFQDN" = "azure.hmpp.root"
"DomainNameNetbios" = "HMPP"
"DomainJoinUsername" = "svc_join_domain"
}
}

if ($DomainNameFQDN -and $ModPlatformADConfigs.ContainsKey($DomainNameFQDN)) {
return $ModPlatformADConfigs.[string]$DomainNameFQDN
}
$Token = Invoke-RestMethod -TimeoutSec 2 -Headers @{"X-aws-ec2-metadata-token-ttl-seconds"=3600} -Method PUT -Uri http://169.254.169.254/latest/api/token
$InstanceId = Invoke-RestMethod -TimeoutSec 2 -Headers @{"X-aws-ec2-metadata-token" = $Token} -Method GET -Uri http://169.254.169.254/latest/meta-data/instance-id
$ModPlatformADSecretRoleName = @{
"EC2HmppsDomainSecretsRole" = @{
"EnvironmentNameTags" = @(
"hmpps-domain-services-development",
"hmpps-domain-services-test",
"hmpps-domain-services-preproduction",
"hmpps-domain-services-production",
"planetfm-development",
"planetfm-test",
"planetfm-preproduction",
"planetfm-production",
"corporate-staff-rostering-development",
"corporate-staff-rostering-test"
"corporate-staff-rostering-preproduction",
"corporate-staff-rostering-production"
)
}
}

$Token = Invoke-RestMethod -TimeoutSec 10 -Headers @{"X-aws-ec2-metadata-token-ttl-seconds"=3600} -Method PUT -Uri http://169.254.169.254/latest/api/token
$InstanceId = Invoke-RestMethod -TimeoutSec 10 -Headers @{"X-aws-ec2-metadata-token" = $Token} -Method GET -Uri http://169.254.169.254/latest/meta-data/instance-id
$TagsRaw = aws ec2 describe-tags --filters "Name=resource-id,Values=$InstanceId"
$Tags = "$TagsRaw" | ConvertFrom-Json
$DomainNameTag = ($Tags.Tags | Where-Object {$_.Key -eq "domain-name"}).Value
$EnvironmentNameTag = ($Tags.Tags | Where-Object {$_.Key -eq "environment-name"}).Value

if ($DomainNameTag -and $ModPlatformADConfigs.containsKey($DomainNameTag)) {
return $ModPlatformADConfigs.[string]$DomainNameTag
$Key = $null
if ($DomainNameFQDN) {
$Key = $DomainNameFQDN
} elseif ($DomainNameTag) {
$Key = $DomainNameTag
} else {
foreach ($Config in $ModPlatformADConfigs.GetEnumerator() ) {
if ($Config.Value["EnvironmentNameTags"].Contains($EnvironmentNameTag)) {
$Key = $Config.Key
break
}
}
}

foreach ($Config in $ModPlatformADConfigs.GetEnumerator() ) {
if ($Config.Value["EnvironmentNameTags"].Contains($EnvironmentNameTag)) {
return $Config.Value
if ($Key) {
if ($ModPlatformADConfigs.ContainsKey($Key)) {
$ConfigCopy = $ModPlatformADConfigs[$Key].Clone()
foreach ($Config in $ModPlatformADSecretRoleName.GetEnumerator() ) {
if ($Config.Value["EnvironmentNameTags"].Contains($EnvironmentNameTag)) {
$ConfigCopy["SecretRoleName"] = $Config.Key
break
}
}
return $ConfigCopy
} else {
Write-Error "No matching configuration for domain ${Key}"
}
}

Write-Error "No matching configuration for environment-name $EnvironmentNameTag"
else {
Write-Error "No matching configuration for environment-name ${EnvironmentNameTag}"
}
}

Export-ModuleMember -Function Get-ModPlatformADConfig
38 changes: 21 additions & 17 deletions powershell/Modules/ModPlatformAD/ModPlatformADCredential.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ function Get-ModPlatformADCredential {
.DESCRIPTION
Using configuration returned from Get-ModPlatformADConfig, this function
assumes a role to access a secret containing the password of the
optionally assumes a role to access a secret containing the password of the
domain join username. EC2 requires permissions to join the given role,
a SSM parameter containing account IDs, and the aws cli.
Expand Down Expand Up @@ -37,23 +37,27 @@ function Get-ModPlatformADCredential {
$SecretAccountId = $AccountIds.[string]$ModPlatformADConfig.SecretAccountName
$SecretName = $ModPlatformADConfig.SecretName
$SecretArn = "arn:aws:secretsmanager:eu-west-2:${SecretAccountId}:secret:${SecretName}"
$AccountId = aws sts get-caller-identity --query Account --output text
$SecretRoleName = $ModPlatformADConfig.SecretRoleName
$RoleArn = "arn:aws:iam::${AccountId}:role/${SecretRoleName}"
$Session = "ModPlatformADConfig-$env:COMPUTERNAME"
$CredsRaw = aws sts assume-role --role-arn "${RoleArn}" --role-session-name "${Session}"
$Creds = "$CredsRaw" | ConvertFrom-Json
$Tmp_AWS_ACCESS_KEY_ID = $env:AWS_ACCESS_KEY_ID
$Tmp_AWS_SECRET_ACCESS_KEY = $env:AWS_SECRET_ACCESS_KEY
$Tmp_AWS_SESSION_TOKEN = $env:AWS_SESSION_TOKEN
$env:AWS_ACCESS_KEY_ID = $Creds.Credentials.AccessKeyId
$env:AWS_SECRET_ACCESS_KEY = $Creds.Credentials.SecretAccessKey
$env:AWS_SESSION_TOKEN = $Creds.Credentials.SessionToken
$SecretValueRaw = aws secretsmanager get-secret-value --secret-id "${SecretArn}" --query SecretString --output text
if ($ModPlatformADConfig.ContainsKey("SecretRoleName")) {
$AccountId = aws sts get-caller-identity --query Account --output text
$SecretRoleName = $ModPlatformADConfig.SecretRoleName
$RoleArn = "arn:aws:iam::${AccountId}:role/${SecretRoleName}"
$Session = "ModPlatformADConfig-$env:COMPUTERNAME"
$CredsRaw = aws sts assume-role --role-arn "${RoleArn}" --role-session-name "${Session}"
$Creds = "$CredsRaw" | ConvertFrom-Json
$Tmp_AWS_ACCESS_KEY_ID = $env:AWS_ACCESS_KEY_ID
$Tmp_AWS_SECRET_ACCESS_KEY = $env:AWS_SECRET_ACCESS_KEY
$Tmp_AWS_SESSION_TOKEN = $env:AWS_SESSION_TOKEN
$env:AWS_ACCESS_KEY_ID = $Creds.Credentials.AccessKeyId
$env:AWS_SECRET_ACCESS_KEY = $Creds.Credentials.SecretAccessKey
$env:AWS_SESSION_TOKEN = $Creds.Credentials.SessionToken
$SecretValueRaw = aws secretsmanager get-secret-value --secret-id "${SecretArn}" --query SecretString --output text
$env:AWS_ACCESS_KEY_ID = $Tmp_AWS_ACCESS_KEY_ID
$env:AWS_SECRET_ACCESS_KEY = $Tmp_AWS_SECRET_ACCESS_KEY
$env:AWS_SESSION_TOKEN = $Tmp_AWS_SESSION_TOKEN
} else {
$SecretValueRaw = aws secretsmanager get-secret-value --secret-id "${SecretArn}" --query SecretString --output text
}
$SecretValue = "$SecretValueRaw" | ConvertFrom-Json
$env:AWS_ACCESS_KEY_ID = $Tmp_AWS_ACCESS_KEY_ID
$env:AWS_SECRET_ACCESS_KEY = $Tmp_AWS_SECRET_ACCESS_KEY
$env:AWS_SESSION_TOKEN = $Tmp_AWS_SESSION_TOKEN
$DomainJoinUsername = $ModPlatformADConfig.DomainJoinUsername
$DomainJoinPassword = $SecretValue.$DomainJoinUsername
if (-not $DomainJoinPassword) {
Expand Down
6 changes: 4 additions & 2 deletions powershell/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ Get-Help Get-ModPlatformADConfig
Put scripts in a `powershell/Scripts` folder. For example, active
directory related scripts in `powershell/Scripts/ModPlatformAD`

### Naming
### Naming

Be consistent. Pascal case (capitalize the first letter of each word) except keywords
and operators which are in lower case.
Expand Down Expand Up @@ -61,8 +61,10 @@ tasks:
Set-Location -Path ([System.IO.Path]::GetTempPath())
$GitBranch = "main"
$Script = "ModPlatformAD/Join-ModPlatformAD.ps1"
$ScriptArgs = @{"NewHostname" = "tag:Name"}
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 # since powershell 4 uses Tls1 as default
Invoke-WebRequest "https://raw.githubusercontent.com/ministryofjustice/modernisation-platform-configuration-management/${GitBranch}/powershell/Scripts/Run-GitScript.ps1" -OutFile "Run-GitScript.ps1"
. ./Run-GitScript.ps1 $Script -GitBranch $GitBranch
. ./Run-GitScript.ps1 $Script -ScriptArgs $ScriptArgs -GitBranch $GitBranch
```

This example downloads the wrapper script and executes it with an example script.
Expand Down
21 changes: 21 additions & 0 deletions powershell/Scripts/Invoke-UserDataScript.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<#
.SYNOPSIS
Get server-type tag and run associated UserDataScript
#>

$ErrorActionPreference = "Stop"
$Token = Invoke-RestMethod -TimeoutSec 10 -Headers @{"X-aws-ec2-metadata-token-ttl-seconds"=3600} -Method PUT -Uri http://169.254.169.254/latest/api/token
$InstanceId = Invoke-RestMethod -TimeoutSec 10 -Headers @{"X-aws-ec2-metadata-token" = $Token} -Method GET -Uri http://169.254.169.254/latest/meta-data/instance-id
$TagsRaw = aws ec2 describe-tags --filters "Name=resource-id,Values=$InstanceId"
$Tags = "$TagsRaw" | ConvertFrom-Json
$ServerTypeTag = ($Tags.Tags | Where-Object {$_.Key -eq "server-type"}).Value
$UserDataScript = "${ServerTypeTag}.ps1"

Set-Location -Path "UserDataScripts"
if (-not $ServerTypeTag) {
Write-Error "Missing or blank server-type tag"
} elseif (-not (Get-ChildItem $UserDataScript -ErrorAction SilentlyContinue)) {
Write-Error "Could not find $UserDataScript"
} else {
. ./$UserDataScript
}
11 changes: 7 additions & 4 deletions powershell/Scripts/ModPlatformAD/Join-ModPlatformAD.ps1
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
<#
.SYNOPSIS
Join computer to appropriate Mod Platform domain
Optionally rename and join computer to appropriate Mod Platform domain
.DESCRIPTION
Either pass in the doman name as a parameter, or derive the AD configuration
from EC2 tags (environment-name or domain-name).
By default the script derives the hostname from the Name tag. Or specify NewHostname parameter.
By default derives the AD configuration from EC2 tags (environment-name or domain-name), or specify DomainNameFQDN parameter.
EC2 requires permissions to get tags and the aws cli.
Exits with 3010 if reboot required and script requires re-running. For use in SSM docs
Expand All @@ -17,6 +17,7 @@

[CmdletBinding()]
param (
[string]$NewHostname = "tag:Name",
[string]$DomainNameFQDN,
[string]$AccountIdsSSMParameterName = "account_ids"
)
Expand All @@ -27,7 +28,9 @@ $ErrorActionPreference = "Stop"

$ADConfig = Get-ModPlatformADConfig -DomainNameFQDN $DomainNameFQDN
$ADCredential = Get-ModPlatformADCredential -ModPlatformADConfig $ADConfig -AccountIdsSSMParameterName $AccountIdsSSMParameterName
if (Rename-ModPlatformADComputer -NewHostname "tag:Name" -ModPlatformADCredential $ADCredential) {
$Renamed = Rename-ModPlatformADComputer -NewHostname $NewHostname -ModPlatformADCredential $ADCredential
if ($Renamed) {
Write-Output "Renamed computer to ${Renamed}"
exit 3010 # triggers reboot if running from SSM Doc
}
if (Add-ModPlatformADComputer -ModPlatformADConfig $ADConfig -ModPlatformADCredential $ADCredential) {
Expand Down
20 changes: 16 additions & 4 deletions powershell/Scripts/Run-GitScript.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@
Clone repo, configure modules and run given powershell script
.PARAMETER Script
Relative path of script from Modules/Scripts directory
Optionally provide a script to run.
Specify relative path of script from Modules/Scripts directory
.PARAMETER ScriptArgs
Optionally provide arguments to the script in hashtable format
.PARAMETER GitBranch
Git branch to checkout, e.g. main
Expand All @@ -15,11 +19,12 @@
Optionally specify location to clone repo, otherwise temp dir is used
.EXAMPLE
Run-GitScript.ps1 ModPlatformAD/Join-ModPlatformAD
Run-GitScript.ps1 -Script "ModPlatformAD/Join-ModPlatformAD" -ScriptArgs @{"DomainNameFQDN": "azure.noms.root"}
#>

param (
[Parameter(Mandatory=$true)][string]$Script,
[string]$Script,
[hashtable]$ScriptArgs,
[string]$GitBranch = "main",
[string]$GitCloneDir
)
Expand Down Expand Up @@ -56,4 +61,11 @@ $ModulePath = Join-Path (Join-Path $GitCloneDir $GitRepo) (Join-Path "powershell
if (-not $env:PSModulePath.Split(";").Contains($ModulePath)) {
$env:PSModulePath = "${env:PSModulePath};${ModulePath}"
}
. powershell/Scripts/$Script
if ($Script) {
$RelativeScriptDir = Split-Path -Parent $Script
$ScriptFilename = Split-Path -Leaf $Script
Set-Location -Path "powershell/Scripts/$RelativeScriptDir"
. ./$ScriptFilename @ScriptArgs
} else {
Set-Location -Path powershell/Scripts
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
. ../ModPlatformAD/Join-ModPlatformAD.ps1

0 comments on commit eb592c2

Please sign in to comment.