I do not know what to attempt anymore, I have about 15 revisions to my Automation runbook. It is very simple and straightforward. I have tried multiple different approaches to get my input parameters and my document parameters to work. But every time I execute, I use rate control so that I can use Targets and specify an instance only, and when I do this I have to choose the parameter that will define how your automation will branch out, which matches my Input parameters 1-to-1. I tried multiple Parameter selection types and various Targets but they all result in the same error: Parameter set cannot be resolved using the specified named parameters. One or more parameters issued cannot be used together or an insufficient number of parameters were provided
I have verified over and over again that my InputPayload corresponds correctly to my Document Parameters and that I am referencing them in all places in my powershell script with the correct syntax.
I am thinking this could be a bug and maybe I should post on GitHub?
I have tested it by running the ps1 script locally on the Windows server instance and the script does not result in the Parameter errors I get when using the AWS SSM Automation script runbook.
UPDATE: The Script field is one line because SSM document Builder for some reason renders it like that which could be the problem. I am just adhering to documentation on how to use Script Runbooks so I am not using Editor to write up the YAML myslef, which could be the problem: DocumentBuilderforScriptRunbooks
description: |-
---
# Automation to domain join Windows EC2 instance
schemaVersion: '0.3'
assumeRole: 'arn:REDACTEDFORPRIVACY/AutomationServiceRole'
parameters:
secretParam:
type: String
default: prod/dhcpwinservers/domainjoin
hostnameParam:
type: String
default: aeawp1046
instanceId:
type: 'AWS::EC2::Instance::Id'
default: i-0983ac4bfa3d22189
mainSteps:
- name: domainjoin
action: 'aws:executeScript'
inputs:
Runtime: PowerShell 7.0
Script: "\n# Install-Module AWSPowerShell -Force\n# Import-Module AWSPowerShell\n\nInstall-Module -Name AWS.Tools.Installer -Force\n# Install-Module AWS.Tools.CloudWatchLogs -Force\n# Install-Module AWS.Tools.SecretsManager -Force\n# Import-Module AWS.Tools.Installer\nInstall-Module -Name AWS.Tools.CloudWatchLogs -Force\nInstall-Module -Name AWS.Tools.SecretsManager -Force\nInstall-Module -Name AWS.Tools.EC2 -Force\nInstall-Module -Name AWS.Tools.SecurityToken -Force\n# Install-AWSToolsModule AWS.Tools.CloudWatchLogs,AWS.Tools.SecretsManager -Force -CleanUp\n# Import-Module AWS.Tools.CloudWatchLogs\n# Import-Module AWS.Tools.SecretsManager\n\n# For using Add-Computer cmdlet\nImport-Module Microsoft.PowerShell.Management -UseWindowsPowerShell -WarningAction SilentlyContinue -SkipEditionCheck -Force\n# For using Cim\n# Import-Module CimCmdlets\n\n\n$inputPayload = $env:InputPayload | ConvertFrom-Json\n[string]$hostnameparam = $inputPayload.host_name\n[string]$secretparam = $inputPayload.secret_set\n\nclass Logger {\n\t#----------------------------------------------\n\t[string] hidden $cwlGroup\n\t[string] hidden $cwlStream\n\t[string] hidden $sequenceToken\n\t#----------------------------------------------\n\t# Log Initialization\n\t#----------------------------------------------\n\tLogger([string] $Action, [string] $HostName) {\n\t\t$this.cwlGroup = \"/ps/boot/configuration/\"\n\t\t$this.cwlStream\t= \"{0}/{1}/{2}\" -f $HostName, $Action,\n\t\t(Get-Date -UFormat \"%Y-%m-%d_%H.%M.%S\")\n\t\t$this.sequenceToken = \"\"\n\t\t#------------------------------------------\n\t\tif ( !(Get-CWLLogGroup -LogGroupNamePrefix $this.cwlGroup) ) {\n\t\t\tNew-CWLLogGroup -LogGroupName $this.cwlGroup\n\t\t\tWrite-CWLRetentionPolicy -LogGroupName $this.cwlGroup -RetentionInDays 3\n\t\t}\n\t\tif ( !(Get-CWLLogStream -LogGroupName $this.cwlGroup -LogStreamNamePrefix $this.cwlStream) ) {\n\t\t\tNew-CWLLogStream -LogGroupName $this.cwlGroup -LogStreamName $this.cwlStream\n\t\t}\n\t}\n\t#----------------------------------------\n\t[void] WriteLine([string] $msg) {\n\t\t$logEntry = New-Object -TypeName \"Amazon.CloudWatchLogs.Model.InputLogEvent\"\n\t\t#-----------------------------------------------------------\n\t\t$logEntry.Message = $msg\n\t\t$logEntry.Timestamp = (Get-Date).ToUniversalTime()\n\t\tif (\"\" -eq $this.sequenceToken) {\n\t\t\t# First write into empty log...\n\t\t\t$this.sequenceToken = Write-CWLLogEvent -LogGroupName $this.cwlGroup `\n\t\t\t\t-LogStreamName $this.cwlStream `\n\t\t\t\t-LogEvent $logEntry\n\t\t}\n\t\telse {\n\t\t\t# Subsequent write into the log...\n\t\t\t$this.sequenceToken = Write-CWLLogEvent -LogGroupName $this.cwlGroup `\n\t\t\t\t-LogStreamName $this.cwlStream `\n\t\t\t\t-SequenceToken $this.sequenceToken `\n\t\t\t\t-LogEvent $logEntry\n\t\t}\n\t}\n}\n[Logger]$log = [Logger]::new(\"UserData\", $hostnameparam)\n$log.WriteLine(\"------------------------------\")\n$log.WriteLine(\"Log Started - V4.0\")\n$RunUser = $env:username\n$log.WriteLine(\"PowerShell session user:\" + $RunUser + \" executed.\")\n$log.WriteLine(\"Loading Secret <\" + $secretparam + \">\")\n$SecretObj = (Get-SECSecretValue -SecretId $secretparam -ErrorVariable SecretObjProcessError)\nif ($SecretObjProcessError) {\n\t$log.WriteLine($SecretObjProcessError)\n $log.WriteLine(\"Could not load secret <\" + $secretparam + \"> - terminating execution\")\n\treturn\n}\n\n[PSCustomObject]$Secret = ($SecretObj.SecretString | ConvertFrom-Json)\n$password = $Secret.password | ConvertTo-SecureString -asPlainText -Force\n$username = $Secret.username\n$credential = New-Object System.Management.Automation.PSCredential($username, $password)\n$log.WriteLine(\"Domain (from Secret): <\" + $Secret.domainname + \">\")\n# Verify domain membership\n$compSys = Get-CimInstance -ClassName Win32_ComputerSystem | Select-Object -Property *\n#------------------------------------------------------------------------------\nif ( ($compSys.PartOfDomain) -and ($compSys.Domain -eq $Secret.domainname)) {\n\t$log.WriteLine(\"Already member of: <\" + $compSys.Domain + \"> - Verifying RSAT Status\")\n\t$RSAT = (Get-WindowsFeature RSAT-AD-PowerShell)\n\tif ($null -eq $RSAT) {\n\t\t$log.WriteLine(\"<RSAT-AD-PowerShell> feature not found - terminating script\")\n\t\treturn\n\t}\n\tif ( (-Not $RSAT.Installed) -and ($RSAT.InstallState -eq \"Available\") ) {\n\t\t$log.WriteLine(\"Installing <RSAT-AD-PowerShell> feature\")\n\t\tInstall-WindowsFeature RSAT-AD-PowerShell\n\t}\n $comProp = (Get-ADComputer -Identity $hostnameparam -Properties Description)\n if ($null -eq $comProp.Description) {\n $log.WriteLine(\"Adding instance metadata to description attribute\")\n $instanceId = Get-EC2InstanceMetadata -Category instanceId\n $instanceAz = Get-EC2InstanceMetadata -Category AvailabilityZone\n $instanceAccountId = (Get-STSCallerIdentity).Account\n\t\t$instanceDescription = $instanceId + \"|\" + $instanceAccountId + \"|\" + $instanceAz\n $log.WriteLine(\"Adding description <\" + $instanceDescription + \">\")\n Set-ADComputer -Identity $hostnameparam -Credential $credential -Description $instanceDescription\n }\n\t# Adding DNS Suffix for WFM domain\n\t$dnssuffixeslist = get-DnsClientGlobalSetting\n\tif ($Secret.domainname -in $dnssuffixeslist.SuffixSearchList) {\n\t\t$log.WriteLine(\"DNS suffix <\" + $Secret.domainname + \"> already exists.\")\n\t}\n\telse {\n\t\t$log.WriteLine(\"Existing DNS suffixes <\" + $dnssuffixeslist.SuffixSearchList + \">\")\n\t\t$log.WriteLine(\"Adding DNS suffix for <\" + $Secret.domainname + \">\")\n\t\t$dnssuffixeslist.SuffixSearchList += $Secret.domainname\n\t\tSet-DnsClientGlobalSetting -SuffixSearchList @($dnssuffixeslist.SuffixSearchList) -ErrorVariable DnsSuffixAddError\n\t\tif ($AddCoDnsSuffixAddErrorputerProcessError) {\n\t\t\t$log.WriteLine($DnsSuffixAddError)\n\t\t\t$log.WriteLine(\"Error occured while adding DNS suffix for <\" + $Secret.domainname + \"> domain\")\n\t\t\treturn\n\t\t}\n\t\t$updateddnssuffixeslist = get-DnsClientGlobalSetting\n\t\t$log.WriteLine(\"Updated DNS suffixes <\" + $updateddnssuffixeslist.SuffixSearchList + \">\")\n\t}\n\t$log.WriteLine(\"Terminating script - \")\n\treturn\n}\n# Performing Domain Join\n$log.WriteLine(\"Performing Domain Join\")\nRename-Computer -NewName $hostnameparam -Force\n$log.WriteLine(\"Attempting to join domain <\" + $Secret.domainname + \">\")\n\nAdd-Computer -ComputerName $hostnameparam -DomainName $Secret.domainname -Credential $credential -OUPath $Secret.oupath -Restart -Force -Verbose -ErrorVariable AddComputerProcessError\nif ($AddComputerProcessError) {\n\t$log.WriteLine($AddComputerProcessError)\n\t$log.WriteLine(\"Error occured while performing domain join operation on host - terminating execution\")\n\treturn\n}\n\n$log.WriteLine(\"Requesting restart...\")"
InputPayload:
host_name: '{{hostnameParam}}'
secret_set: '{{secretParam}}'
outputs:
- Selector: $.Payload
Name: payload
Type: StringMap
UPDATE 2: I modified the Automation document using Editor to just input my YAML directly and it still fails on the Parameters. I am convinced this is a AWS SSM bug. Sucks.
description: |-
---
# Automation to domain join Windows EC2 instance
schemaVersion: '0.3'
assumeRole: 'arn:aws:iam::186895353292:role/AutomationServiceRole'
parameters:
secretParam:
type: String
default: prod/dhcpwinservers/domainjoin
hostnameParam:
type: String
default: aeawp1046
mainSteps:
- name: domainjoin
action: 'aws:executeScript'
inputs:
Runtime: PowerShell 7.0
InputPayload:
host_name: '{{hostnameParam}}'
secret_set: '{{secretParam}}'
Script: |-
# Install-Module AWSPowerShell -Force
# Import-Module AWSPowerShell
Install-Module -Name AWS.Tools.Installer -Force
# Install-Module AWS.Tools.CloudWatchLogs -Force
# Install-Module AWS.Tools.SecretsManager -Force
# Import-Module AWS.Tools.Installer
Install-Module -Name AWS.Tools.CloudWatchLogs -Force
Install-Module -Name AWS.Tools.SecretsManager -Force
Install-Module -Name AWS.Tools.EC2 -Force
Install-Module -Name AWS.Tools.SecurityToken -Force
# Install-AWSToolsModule AWS.Tools.CloudWatchLogs,AWS.Tools.SecretsManager -Force -CleanUp
# Import-Module AWS.Tools.CloudWatchLogs
# Import-Module AWS.Tools.SecretsManager
# For using Add-Computer cmdlet
Import-Module Microsoft.PowerShell.Management -UseWindowsPowerShell -WarningAction SilentlyContinue -SkipEditionCheck -Force
# For using Cim
# Import-Module CimCmdlets
$inputPayload = $env:InputPayload | ConvertFrom-Json
[string]$hostnameparam = $inputPayload.host_name
[string]$secretparam = $inputPayload.secret_set
class Logger {
#----------------------------------------------
[string] hidden $cwlGroup
[string] hidden $cwlStream
[string] hidden $sequenceToken
#----------------------------------------------
# Log Initialization
#----------------------------------------------
Logger([string] $Action, [string] $HostName) {
$this.cwlGroup = "/ps/boot/configuration/"
$this.cwlStream = "{0}/{1}/{2}" -f $HostName, $Action,
(Get-Date -UFormat "%Y-%m-%d_%H.%M.%S")
$this.sequenceToken = ""
#------------------------------------------
if ( !(Get-CWLLogGroup -LogGroupNamePrefix $this.cwlGroup) ) {
New-CWLLogGroup -LogGroupName $this.cwlGroup
Write-CWLRetentionPolicy -LogGroupName $this.cwlGroup -RetentionInDays 3
}
if ( !(Get-CWLLogStream -LogGroupName $this.cwlGroup -LogStreamNamePrefix $this.cwlStream) ) {
New-CWLLogStream -LogGroupName $this.cwlGroup -LogStreamName $this.cwlStream
}
}
#----------------------------------------
[void] WriteLine([string] $msg) {
$logEntry = New-Object -TypeName "Amazon.CloudWatchLogs.Model.InputLogEvent"
#-----------------------------------------------------------
$logEntry.Message = $msg
$logEntry.Timestamp = (Get-Date).ToUniversalTime()
if ("" -eq $this.sequenceToken) {
# First write into empty log...
$this.sequenceToken = Write-CWLLogEvent -LogGroupName $this.cwlGroup `
-LogStreamName $this.cwlStream `
-LogEvent $logEntry
}
else {
# Subsequent write into the log...
$this.sequenceToken = Write-CWLLogEvent -LogGroupName $this.cwlGroup `
-LogStreamName $this.cwlStream `
-SequenceToken $this.sequenceToken `
-LogEvent $logEntry
}
}
}
[Logger]$log = [Logger]::new("UserData", $hostnameparam)
$log.WriteLine("------------------------------")
$log.WriteLine("Log Started - V4.0")
$RunUser = $env:username
$log.WriteLine("PowerShell session user:" + $RunUser + " executed.")
$log.WriteLine("Loading Secret <" + $secretparam + ">")
$SecretObj = (Get-SECSecretValue -SecretId $secretparam -ErrorVariable SecretObjProcessError)
if ($SecretObjProcessError) {
$log.WriteLine($SecretObjProcessError)
$log.WriteLine("Could not load secret <" + $secretparam + "> - terminating execution")
return
}
[PSCustomObject]$Secret = ($SecretObj.SecretString | ConvertFrom-Json)
$password = $Secret.password | ConvertTo-SecureString -asPlainText -Force
$username = $Secret.username
$credential = New-Object System.Management.Automation.PSCredential($username, $password)
$log.WriteLine("Domain (from Secret): <" + $Secret.domainname + ">")
# Verify domain membership
$compSys = Get-CimInstance -ClassName Win32_ComputerSystem | Select-Object -Property *
#------------------------------------------------------------------------------
if ( ($compSys.PartOfDomain) -and ($compSys.Domain -eq $Secret.domainname)) {
$log.WriteLine("Already member of: <" + $compSys.Domain + "> - Verifying RSAT Status")
$RSAT = (Get-WindowsFeature RSAT-AD-PowerShell)
if ($null -eq $RSAT) {
$log.WriteLine("<RSAT-AD-PowerShell> feature not found - terminating script")
return
}
if ( (-Not $RSAT.Installed) -and ($RSAT.InstallState -eq "Available") ) {
$log.WriteLine("Installing <RSAT-AD-PowerShell> feature")
Install-WindowsFeature RSAT-AD-PowerShell
}
$comProp = (Get-ADComputer -Identity $hostnameparam -Properties Description)
if ($null -eq $comProp.Description) {
$log.WriteLine("Adding instance metadata to description attribute")
$instanceId = Get-EC2InstanceMetadata -Category instanceId
$instanceAz = Get-EC2InstanceMetadata -Category AvailabilityZone
$instanceAccountId = (Get-STSCallerIdentity).Account
$instanceDescription = $instanceId + "|" + $instanceAccountId + "|" + $instanceAz
$log.WriteLine("Adding description <" + $instanceDescription + ">")
Set-ADComputer -Identity $hostnameparam -Credential $credential -Description $instanceDescription
}
# Adding DNS Suffix for WFM domain
$dnssuffixeslist = get-DnsClientGlobalSetting
if ($Secret.domainname -in $dnssuffixeslist.SuffixSearchList) {
$log.WriteLine("DNS suffix <" + $Secret.domainname + "> already exists.")
}
else {
$log.WriteLine("Existing DNS suffixes <" + $dnssuffixeslist.SuffixSearchList + ">")
$log.WriteLine("Adding DNS suffix for <" + $Secret.domainname + ">")
$dnssuffixeslist.SuffixSearchList += $Secret.domainname
Set-DnsClientGlobalSetting -SuffixSearchList @($dnssuffixeslist.SuffixSearchList) -ErrorVariable DnsSuffixAddError
if ($AddCoDnsSuffixAddErrorputerProcessError) {
$log.WriteLine($DnsSuffixAddError)
$log.WriteLine("Error occured while adding DNS suffix for <" + $Secret.domainname + "> domain")
return
}
$updateddnssuffixeslist = get-DnsClientGlobalSetting
$log.WriteLine("Updated DNS suffixes <" + $updateddnssuffixeslist.SuffixSearchList + ">")
}
$log.WriteLine("Terminating script - ")
return
}
# Performing Domain Join
$log.WriteLine("Performing Domain Join")
Rename-Computer -NewName $hostnameparam -Force
$log.WriteLine("Attempting to join domain <" + $Secret.domainname + ">")
Add-Computer -ComputerName $hostnameparam -DomainName $Secret.domainname -Credential $credential -OUPath $Secret.oupath -Restart -Force -Verbose -ErrorVariable AddComputerProcessError
if ($AddComputerProcessError) {
$log.WriteLine($AddComputerProcessError)
$log.WriteLine("Error occured while performing domain join operation on host - terminating execution")
return
}
$log.WriteLine("Requesting restart...")