13

I'm provisioning a single windows server for testing with terraform in AWS. Every time i need to decrypt my windows password with my PEM file to connect. Instead, i chose the terraform argument get_password_data and stored my password_data in tfstate file. Now how do i decrypt the same with interpolation syntax rsadecrypt

Please find my below terraform code

### Resource for EC2 instance creation ###

resource "aws_instance" "ec2" {
  ami                   =   "${var.ami}"
  instance_type         =   "${var.instance_type}"
  key_name              =   "${var.key_name}"
  subnet_id             =   "${var.subnet_id}"
  security_groups       =  ["${var.security_groups}"]
  availability_zone     =   "${var.availability_zone}"
  private_ip            =   "x.x.x.x"
  get_password_data     =   "true"

  connection {
    password            =   "${rsadecrypt(self.password_data)}"
    }

  root_block_device {
              volume_type = "${var.volume_type}"
              volume_size = "${var.volume_size}"
    delete_on_termination = "true"
    }

  tags {
        "Cost Center"  =  "R1"
        "Name"         =  "AD-test"
        "Purpose"      =  "Task"
        "Server Name"  =  "Active Directory"
        "SME Name"     =  "Ravi"
    }

}


output "instance_id" {
  value = "${aws_instance.ec2.id}"
}


### Resource for EBS volume creation ###

  resource "aws_ebs_volume" "additional_vol" {
    availability_zone =  "${var.availability_zone}"
    size              =  "${var.size}"
    type              =  "${var.type}"
}

### Output of Volume ID ###

  output "vol_id" {
    value = "${aws_ebs_volume.additional_vol.id}"
}

### Resource for Volume attachment ###

   resource "aws_volume_attachment" "attach_vol" {
     device_name       = "${var.device_name}"
     volume_id         = "${aws_ebs_volume.additional_vol.id}"
     instance_id       = "${aws_instance.ec2.id}"
     skip_destroy      = "true"
}
Daniel Mann
  • 57,011
  • 13
  • 100
  • 120
Ravichandran
  • 427
  • 1
  • 3
  • 16

3 Answers3

11

The password is encrypted using the key_pair you specified when launching the instance, you still need to use it to decrypt as password_data is still just the base64 encoded encrypted password data.

You should use ${rsadecrypt(self.password_data,file("/path/to/private_key.pem"))}

This is for good reason. You really don't want just a base64 encoded password floating around in state.

Short version: You are missing the second argument in the interpolation function.

mootpt
  • 298
  • 3
  • 11
  • Error: Error refreshing state: 1 error(s) occurred: * module.Windows_ec2.output.ec2_password: rsadecrypt: crypto/rsa: decryption error in: ${rsadecrypt(aws_instance.ec2.password_data, file("C:/terraform/Task1/test.pem"))} – Ravichandran Jul 02 '18 at 05:26
  • I got the below error when using the `self.password_data` so i made a change. Error: output 'ec2_password': cannot contain self-reference self.password_data – Ravichandran Jul 02 '18 at 05:28
  • Nice to see you resolved the first error. The second error seems to suggest something went wrong with decryption. Can you confirm that `C:\terraform\Task1\test.pem` is the private key of the keypair used when launching the instance? Also, on that note, I noticed you used forward slashes instead of backslashes ;) not sure it matters, but it might. – mootpt Jul 02 '18 at 06:17
  • Yes, i confirm that i'm using the correct key. Also if i use the backslash, it throws me an error like `illegal char escape`. Forward slash should be used here. – Ravichandran Jul 02 '18 at 07:37
  • Let me test this myself and get back to you. Maybe something wrong with the interpolation function. I know it's fairly new. – mootpt Jul 02 '18 at 18:33
  • 3
    Just tested this and it appears to work. Check out my gist: https://gist.github.com/mootpt/1ea7aac43e90d27ab890d45bc3b1d3d5. All of this was done on Terraform 0.11.7. Can you confirm your version? – mootpt Jul 02 '18 at 19:41
  • 2
    This comes to an end. its working as expected and could see the decrypted password in a state file. **Below is the working code:** `output "ec2_password" { value = "${rsadecrypt(aws_instance.ec2.password_data, file("C:/terraform/Task1/BOI-BOI.pem"))}"` – Ravichandran Jul 03 '18 at 06:05
  • Thanks for this, I was looking all day for a solution to this problem. – C.J. May 31 '19 at 21:23
  • @mootpt I wonder if you can suggest how to fix the decryption when I have multiple windows instances (https://stackoverflow.com/questions/56884621/how-to-fix-rsadecrypt-argument-1-should-be-type-string-got-type-list-in) – Anthony Kong Jul 04 '19 at 23:18
1

Rather than having .pem files lying around or explicitly inputting a public key, you can generate the key directly with tls_private_key and then directly copy the resulting password into AWS SSM Parameter Store so you can retrieve it from there after your infrastructure is stood up.

Here's the way I generate the key:

resource "tls_private_key" "instance_key" {
  algorithm = "RSA"
}

resource "aws_key_pair" "instance_key_pair" {
  key_name   = "${local.name_prefix}-instance-key"
  public_key = tls_private_key.instance_key.public_key_openssh
}

In your aws_instance you want to be sure these are set:

  key_name                = aws_key_pair.instance_key_pair.key_name
  get_password_data       = true

Finally, store the resulting password in SSM (NOTE: you need to wrap the private key nonsensitive):

resource "aws_ssm_parameter" "windows_ec2" {
  depends_on = [aws_instance.winserver_instance[0]]
  name       = "/Microsoft/AD/${var.environment}/ec2-win-password"
  type       = "SecureString"
  value = rsadecrypt(aws_instance.winserver_instance[0].password_data, nonsensitive(tls_private_key.instance_key
  .private_key_pem))
}
occasl
  • 5,303
  • 5
  • 56
  • 81
0

I know this is not related to the actual question but it might be useful if you don't want to expose your private key in a public environment (e.g.. Git)

I would rather print the encrypted password

resource "aws_instance" "ec2" {
    ami = .....
    instance_type = .....
    security_groups = [.....]
    subnet_id = .....
    iam_instance_profile = .....
    key_name = .....
    get_password_data = "true"
    tags = {
        Name = .....
    }
}

Like this

output "Administrator_Password" {
   value = [
     aws_instance.ec2.password_data
   ]
 }

Then,

  • Get base64 password and put it in a file called pwdbase64.txt

  • Run this command to decode the base64 to bin file

    certutil -decode pwdbase64.txt password.bin

  • Run this command to decrypt your password.bin

    openssl rsautl -decrypt -inkey privatekey.openssh -in password.bin

If you don't know how to play with openssl. Please check this post

privatekey.openssh should look like:

-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQCd+qQbLiSVuNludd67EtepR3g1+VzV6gjsZ+Q+RtuLf88cYQA3
6M4rjVAy......1svfaU/powWKk7WWeE58dnnTZoLvHQ
ZUvFlHE/LUHCQkx8sSECQGatJGiS5fgZhvpzLn4amNwKkozZ3tc02fMzu8IgdEit
jrk5Zq8Vg71vH1Z5OU0kjgrR4ZCjG9ngGdaFV7K7ki0=
-----END RSA PRIVATE KEY-----

public key should look like:

ssh-rsa AAAAB3NzaC1yc2EAAAADAQAB......iFZmwQ==

terraform key pair code should look like

resource "aws_key_pair" "key_pair_ec2" {
   key_name = "key_pair_ec2"
   public_key = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQAB......iFZmwQ=="
}

Pd: You can use puttygen to generate the keys