Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

f/instance d/instance: Get (Windows) password data #2219

Merged
merged 4 commits into from
Mar 13, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 22 additions & 1 deletion aws/data_source_aws_instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,15 @@ func dataSourceAwsInstance() *schema.Resource {
Type: schema.TypeString,
Computed: true,
},
"get_password_data": {
Type: schema.TypeBool,
Optional: true,
Default: false,
},
"password_data": {
Type: schema.TypeString,
Computed: true,
},
"public_dns": {
Type: schema.TypeString,
Computed: true,
Expand Down Expand Up @@ -266,7 +275,19 @@ func dataSourceAwsInstanceRead(d *schema.ResourceData, meta interface{}) error {
}

log.Printf("[DEBUG] aws_instance - Single Instance ID found: %s", *instance.InstanceId)
return instanceDescriptionAttributes(d, instance, conn)
if err := instanceDescriptionAttributes(d, instance, conn); err != nil {
return err
}

if d.Get("get_password_data").(bool) {
passwordData, err := getAwsEc2InstancePasswordData(*instance.InstanceId, conn)
if err != nil {
return err
}
d.Set("password_data", passwordData)
}

return nil
}

// Populate instance attribute fields with the returned instance
Expand Down
86 changes: 86 additions & 0 deletions aws/data_source_aws_instance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,56 @@ func TestAccAWSInstanceDataSource_VPCSecurityGroups(t *testing.T) {
})
}

func TestAccAWSInstanceDataSource_getPasswordData_trueToFalse(t *testing.T) {
rInt := acctest.RandInt()

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
Steps: []resource.TestStep{
{
Config: testAccInstanceDataSourceConfig_getPasswordData(true, rInt),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("data.aws_instance.foo", "get_password_data", "true"),
resource.TestCheckResourceAttrSet("data.aws_instance.foo", "password_data"),
),
},
{
Config: testAccInstanceDataSourceConfig_getPasswordData(false, rInt),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("data.aws_instance.foo", "get_password_data", "false"),
resource.TestCheckNoResourceAttr("data.aws_instance.foo", "password_data"),
),
},
},
})
}

func TestAccAWSInstanceDataSource_getPasswordData_falseToTrue(t *testing.T) {
rInt := acctest.RandInt()

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
Steps: []resource.TestStep{
{
Config: testAccInstanceDataSourceConfig_getPasswordData(false, rInt),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("data.aws_instance.foo", "get_password_data", "false"),
resource.TestCheckNoResourceAttr("data.aws_instance.foo", "password_data"),
),
},
{
Config: testAccInstanceDataSourceConfig_getPasswordData(true, rInt),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("data.aws_instance.foo", "get_password_data", "true"),
resource.TestCheckResourceAttrSet("data.aws_instance.foo", "password_data"),
),
},
},
})
}

// Lookup based on InstanceID
const testAccInstanceDataSourceConfig = `
resource "aws_instance" "web" {
Expand Down Expand Up @@ -502,3 +552,39 @@ data "aws_instance" "foo" {
instance_id = "${aws_instance.foo_instance.id}"
}
`

func testAccInstanceDataSourceConfig_getPasswordData(val bool, rInt int) string {
return fmt.Sprintf(`
# Find latest Microsoft Windows Server 2016 Core image (Amazon deletes old ones)
data "aws_ami" "win2016core" {
most_recent = true

filter {
name = "owner-alias"
values = ["amazon"]
}

filter {
name = "name"
values = ["Windows_Server-2016-English-Core-Base-*"]
}
}

resource "aws_key_pair" "foo" {
key_name = "tf-acctest-%d"
public_key = "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAQEAq6U3HQYC4g8WzU147gZZ7CKQH8TgYn3chZGRPxaGmHW1RUwsyEs0nmombmIhwxudhJ4ehjqXsDLoQpd6+c7BuLgTMvbv8LgE9LX53vnljFe1dsObsr/fYLvpU9LTlo8HgHAqO5ibNdrAUvV31ronzCZhms/Gyfdaue88Fd0/YnsZVGeOZPayRkdOHSpqme2CBrpa8myBeL1CWl0LkDG4+YCURjbaelfyZlIApLYKy3FcCan9XQFKaL32MJZwCgzfOvWIMtYcU8QtXMgnA3/I3gXk8YDUJv5P4lj0s/PJXuTM8DygVAUtebNwPuinS7wwonm5FXcWMuVGsVpG5K7FGQ== tf-acc-winpasswordtest"
}

resource "aws_instance" "foo" {
ami = "${data.aws_ami.win2016core.id}"
instance_type = "t2.medium"
key_name = "${aws_key_pair.foo.key_name}"
}

data "aws_instance" "foo" {
instance_id = "${aws_instance.foo.id}"

get_password_data = %t
}
`, rInt, val)
}
53 changes: 53 additions & 0 deletions aws/resource_aws_instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,17 @@ func resourceAwsInstance() *schema.Resource {
Computed: true,
},

"get_password_data": {
Type: schema.TypeBool,
Optional: true,
Default: false,
},

"password_data": {
Type: schema.TypeString,
Computed: true,
},

"subnet_id": {
Type: schema.TypeString,
Optional: true,
Expand Down Expand Up @@ -759,6 +770,17 @@ func resourceAwsInstanceRead(d *schema.ResourceData, meta interface{}) error {
}
}

if d.Get("get_password_data").(bool) {
passwordData, err := getAwsEc2InstancePasswordData(*instance.InstanceId, conn)
if err != nil {
return err
}
d.Set("password_data", passwordData)
} else {
d.Set("get_password_data", false)
d.Set("password_data", nil)
}

return nil
}

Expand Down Expand Up @@ -1528,6 +1550,37 @@ func readSecurityGroups(d *schema.ResourceData, instance *ec2.Instance, conn *ec
return nil
}

func getAwsEc2InstancePasswordData(instanceID string, conn *ec2.EC2) (string, error) {
log.Printf("[INFO] Reading password data for instance %s", instanceID)

var passwordData string

err := resource.Retry(15*time.Minute, func() *resource.RetryError {
resp, err := conn.GetPasswordData(&ec2.GetPasswordDataInput{
InstanceId: aws.String(instanceID),
})

if err != nil {
return resource.NonRetryableError(err)
}

if resp.PasswordData == nil || *resp.PasswordData == "" {
return resource.RetryableError(fmt.Errorf("Password data is blank for instance ID: %s", instanceID))
}

passwordData = strings.TrimSpace(*resp.PasswordData)

log.Printf("[INFO] Password data read for instance %s", instanceID)
return nil
})

if err != nil {
return "", err
}

return passwordData, nil
}

type awsInstanceOpts struct {
BlockDeviceMappings []*ec2.BlockDeviceMapping
DisableAPITermination *bool
Expand Down
94 changes: 94 additions & 0 deletions aws/resource_aws_instance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1288,6 +1288,68 @@ func TestAccAWSInstance_associatePublic_overridePrivate(t *testing.T) {
})
}

func TestAccAWSInstance_getPasswordData_falseToTrue(t *testing.T) {
var before, after ec2.Instance
resName := "aws_instance.foo"
rInt := acctest.RandInt()

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckInstanceDestroy,
Steps: []resource.TestStep{
{
Config: testAccInstanceConfig_getPasswordData(false, rInt),
Check: resource.ComposeTestCheckFunc(
testAccCheckInstanceExists(resName, &before),
resource.TestCheckResourceAttr(resName, "get_password_data", "false"),
resource.TestCheckResourceAttr(resName, "password_data", ""),
),
},
{
Config: testAccInstanceConfig_getPasswordData(true, rInt),
Check: resource.ComposeTestCheckFunc(
testAccCheckInstanceExists(resName, &after),
testAccCheckInstanceNotRecreated(t, &before, &after),
resource.TestCheckResourceAttr(resName, "get_password_data", "true"),
resource.TestCheckResourceAttrSet(resName, "password_data"),
),
},
},
})
}

func TestAccAWSInstance_getPasswordData_trueToFalse(t *testing.T) {
var before, after ec2.Instance
resName := "aws_instance.foo"
rInt := acctest.RandInt()

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckInstanceDestroy,
Steps: []resource.TestStep{
{
Config: testAccInstanceConfig_getPasswordData(true, rInt),
Check: resource.ComposeTestCheckFunc(
testAccCheckInstanceExists(resName, &before),
resource.TestCheckResourceAttr(resName, "get_password_data", "true"),
resource.TestCheckResourceAttrSet(resName, "password_data"),
),
},
{
Config: testAccInstanceConfig_getPasswordData(false, rInt),
Check: resource.ComposeTestCheckFunc(
testAccCheckInstanceExists(resName, &after),
testAccCheckInstanceNotRecreated(t, &before, &after),
resource.TestCheckResourceAttr(resName, "get_password_data", "false"),
resource.TestCheckResourceAttr(resName, "password_data", ""),
),
},
},
})
}

func testAccCheckInstanceNotRecreated(t *testing.T,
before, after *ec2.Instance) resource.TestCheckFunc {
return func(s *terraform.State) error {
Expand Down Expand Up @@ -2719,3 +2781,35 @@ resource "aws_instance" "foo" {
}
}`, rInt, rInt)
}

func testAccInstanceConfig_getPasswordData(val bool, rInt int) string {
return fmt.Sprintf(`
# Find latest Microsoft Windows Server 2016 Core image (Amazon deletes old ones)
data "aws_ami" "win2016core" {
most_recent = true

filter {
name = "owner-alias"
values = ["amazon"]
}

filter {
name = "name"
values = ["Windows_Server-2016-English-Core-Base-*"]
}
}

resource "aws_key_pair" "foo" {
key_name = "tf-acctest-%d"
public_key = "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAQEAq6U3HQYC4g8WzU147gZZ7CKQH8TgYn3chZGRPxaGmHW1RUwsyEs0nmombmIhwxudhJ4ehjqXsDLoQpd6+c7BuLgTMvbv8LgE9LX53vnljFe1dsObsr/fYLvpU9LTlo8HgHAqO5ibNdrAUvV31ronzCZhms/Gyfdaue88Fd0/YnsZVGeOZPayRkdOHSpqme2CBrpa8myBeL1CWl0LkDG4+YCURjbaelfyZlIApLYKy3FcCan9XQFKaL32MJZwCgzfOvWIMtYcU8QtXMgnA3/I3gXk8YDUJv5P4lj0s/PJXuTM8DygVAUtebNwPuinS7wwonm5FXcWMuVGsVpG5K7FGQ== tf-acc-winpasswordtest"
}

resource "aws_instance" "foo" {
ami = "${data.aws_ami.win2016core.id}"
instance_type = "t2.medium"
key_name = "${aws_key_pair.foo.key_name}"

get_password_data = %t
}
`, rInt, val)
}
6 changes: 6 additions & 0 deletions website/docs/d/instance.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ exactly match a pair on the desired Instance.
several valid keys, for a full reference, check out
[describe-instances in the AWS CLI reference][1].

* `get_password_data` - (Optional) If true, wait for password data to become available and retrieve it. Useful for getting the administrator password for instances running Microsoft Windows. The password data is exported to the `password_data` attribute. See [GetPasswordData](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_GetPasswordData.html) for more information.

~> **NOTE:** At least one of `filter`, `instance_tags`, or `instance_id` must be specified.

~> **NOTE:** If anything other than a single match is returned by the search,
Expand Down Expand Up @@ -75,6 +77,10 @@ interpolation.
* `key_name` - The key name of the Instance.
* `monitoring` - Whether detailed monitoring is enabled or disabled for the Instance (Boolean).
* `network_interface_id` - The ID of the network interface that was created with the Instance.
* `password_data` - Base-64 encoded encrypted password data for the instance.
Useful for getting the administrator password for instances running Microsoft Windows.
This attribute is only exported if `get_password_data` is true.
See [GetPasswordData](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_GetPasswordData.html) for more information.
* `placement_group` - The placement group of the Instance.
* `private_dns` - The private DNS name assigned to the Instance. Can only be
used inside the Amazon EC2, and only available if you've enabled DNS hostnames
Expand Down
6 changes: 6 additions & 0 deletions website/docs/r/instance.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ instance. Amazon defaults this to `stop` for EBS-backed instances and
instances. See [Shutdown Behavior](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/terminating-instances.html#Using_ChangingInstanceInitiatedShutdownBehavior) for more information.
* `instance_type` - (Required) The type of instance to start
* `key_name` - (Optional) The key name to use for the instance.
* `get_password_data` - (Optional) If true, wait for password data to become available and retrieve it. Useful for getting the administrator password for instances running Microsoft Windows. The password data is exported to the `password_data` attribute. See [GetPasswordData](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_GetPasswordData.html) for more information.
* `monitoring` - (Optional) If true, the launched EC2 instance will have detailed monitoring enabled. (Available since v0.6.0)
* `security_groups` - (Optional) A list of security group names to associate with.
If you are creating Instances in a VPC, use `vpc_security_group_ids` instead.
Expand Down Expand Up @@ -220,6 +221,11 @@ The following attributes are exported:
* `availability_zone` - The availability zone of the instance.
* `placement_group` - The placement group of the instance.
* `key_name` - The key name of the instance
* `password_data` - Base-64 encoded encrypted password data for the instance.
Useful for getting the administrator password for instances running Microsoft Windows.
This attribute is only exported if `get_password_data` is true.
Note that this encrypted value will be stored in the state file, as with all exported attributes.
See [GetPasswordData](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_GetPasswordData.html) for more information.
* `public_dns` - The public DNS name assigned to the instance. For EC2-VPC, this
is only available if you've enabled DNS hostnames for your VPC
* `public_ip` - The public IP address assigned to the instance, if applicable. **NOTE**: If you are using an [`aws_eip`](/docs/providers/aws/r/eip.html) with your instance, you should refer to the EIP's address directly and not use `public_ip`, as this field will change after the EIP is attached.
Expand Down