0

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...")
  • Put a semicolon at the end of each command. Define the script as a variable : $script = `@'( put script here)'@. Replace the \n with real returns. What you have is not usable!!! Making the script one line is wrong. For continuation lines you can add a space with a backtick (on the same key as the tilda). – jdweng Mar 01 '23 at 16:44
  • I edited my comment to show the actual script input. My original post had some formatting that does not actually represent the script input. – humbleStrength Mar 01 '23 at 16:52
  • The Script field one line input with all the \n characters is what is in the Content section in my Automation Document for some reason after I save it. I do not know why it renders it like that. The second code section in my post shows how I input the Script when building the Document. – humbleStrength Mar 01 '23 at 16:56
  • Is the at the full error message? Usually the error includes details about the call site (eg. which exact command was executed) – Mathias R. Jessen Mar 01 '23 at 16:57
  • @MathiasR.Jessen because I am running this from AWS SSM as an automation runbook, I get limited error output. When I run it directly on the host, the script works as intended. So I think that means that aws ssm is not parsing my parameters correctly. Like @jdweng was explaining to an extent. For some reason it is rendering my script input into the one line output from my original post. The only error I can see is `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` – humbleStrength Mar 01 '23 at 17:04
  • You could remove -WarningAction SilentlyContinue to get warnings. – jdweng Mar 01 '23 at 17:49
  • Insert line `throw 'new error';` at the very start of the script. Does the document now fail with a "new error"? if so, move that line through your script to figure out where its failing. Pretty sure you can use `Write-Host` in your script too to get some context – MisterSmith Mar 01 '23 at 19:46
  • @MisterSmith I will attempt this. Thanks. – humbleStrength Mar 02 '23 at 15:13

1 Answers1

0

The parameter problem was due to an incompatibility on this line:

Import-Module Microsoft.PowerShell.Management -UseWindowsPowerShell -WarningAction SilentlyContinue -SkipEditionCheck -Force

I changed it to this and it got passed the error:

Import-Module Microsoft.PowerShell.Management -UseWindowsPowerShell -WarningAction SilentlyContinue

There are still problems with 3 of my cmdlets using Powershell 7 Runtime, for some reason they work when doing the Import and running them locally on the instance, but do not work in the Automation document:

Get-CimInstance.... Rename-Computer.... Add-Computer....

Going to make a separate post for this issue.