ezshield / posh-awscfn

PowerShell module to help generate AWS CloudFormation templates
MIT License
12 stars 2 forks source link

More examples #1

Open theonlyway opened 7 years ago

theonlyway commented 7 years ago

Hi,

I've just recently started looking in to trying to create some cloudformation templates using PowerShell and stumbled over this module. On the surface it looks great but I'm having some issues trying to figure some of the stuff out.

Using the Cloudformation designer it's pretty easy to create resources and their discrepancies.

Example

Which ends up looking something like this

  "Resources": {
     "SampleVPC1": {
       "Type": "AWS::EC2::VPC",
       "Properties": {},
       "Metadata": {
         "AWS::CloudFormation::Designer": {
           "id": "e61f601f-0cbc-4e51-91af-1cd3a1069ca1"
         }
       }
     },
     "SampleVPC2": {
       "Type": "AWS::EC2::VPC",
       "Properties": {},
       "Metadata": {
         "AWS::CloudFormation::Designer": {
           "id": "6edcdbde-97ff-402b-bf32-4bac177f2350"
         }
       }
     },
     "VPC1Subnet1": {
       "Type": "AWS::EC2::Subnet",
       "Properties": {
         "VpcId": {
           "Ref": "SampleVPC1"
         }
       },
       "Metadata": {
         "AWS::CloudFormation::Designer": {
           "id": "461c9c16-0ca3-4365-b37c-0bf5fb37bd67"
         }
       },
       "DependsOn": [
         "SampleVPC1"
       ]
     },
     "VPC2Subnet1": {
       "Type": "AWS::EC2::Subnet",
       "Properties": {
         "VpcId": {
           "Ref": "SampleVPC2"
         }
       },

Are you able to give me some examples of how to get the module to generate the "Ref": "SampleVPC2"?

Thanks Anthony

ebekker commented 7 years ago

Hope you don't mind, I edited your comment to make the code sample more readable.

So are you just asking me for a sample in PowerShell via the awscfn module for the SampleVPC2 resource in the config above?

ebekker commented 7 years ago

So here's the awscfn fragment that would correspond to the whole sample you provided, I'm using the typed-resource notation, which helps me identify the resource-specific parameters via IntelliSense in something like the PowerShell ISE (or even the CLI). For the sake of completeness, I've even included the metadata generated by the CFN Designer, which you wouldn't normally have to put in there if you are sticking strictly to using this tool:

Template -JSON -Description "Sample for issue #1 request" -JSON {

    Res-EC2-VPC SampleVPC1 -Metadata @{
        "AWS::CloudFormation::Designer" = @{
            id = "e61f601f-0cbc-4e51-91af-1cd3a1069ca1"
        }
    }

    Res-EC2-VPC SampleVPC2 -Metadata @{
        "AWS::CloudFormation::Designer" = @{
            id = "6edcdbde-97ff-402b-bf32-4bac177f2350"
        }
    }

    Res-EC2-Subnet VPC1Subnet1 -VpcId (Fn-Ref SampleVPC1) -DependsOn SampleVPC1 -Metadata @{
        "AWS::CloudFormation::Designer" = @{
            id = "461c9c16-0ca3-4365-b37c-0bf5fb37bd67"
        }
    }

    Res-EC2-Subnet VPC2Subnet1 -VpcId (Fn-Ref SampleVPC1) -DependsOn SampleVPC1 -Metadata @{
        "AWS::CloudFormation::Designer" = @{
            id = "461c9c16-0ca3-4365-b37c-0bf5fb37bd67"
        }
    }
}
ebekker commented 7 years ago

UPDATE: THIS IS WRONG, UPDATED IN LATER COMMENT BELOW!

If you take out the unnecessary Designer meta data, it becomes more compact.

Here's another sample that fills out your JSON fragment with a few property parameters to the resources you provided to make it a bit more real-world:

Template -JSON -Description "Sample for issue #1 request" {

    Res-EC2-VPC SampleVPC1 -CidrBlock 10.10.0.0/16 `
        -InstanceTenancy dedicated `
        -EnableDnsSupport $true `
        -Tags @{
            MyNote = "Production network"
        }

    Res-EC2-VPC SampleVPC2 -CidrBlock 10.20.0.0/16 `
        -Tags @{
            MyNote = "Development network"
        }

    Res-EC2-Subnet VPC1Subnet1 -DependsOn SampleVPC1 `
        -VpcId (Fn-Ref SampleVPC1) `
        -AvailabilityZone us-east-1a -CidrBlock 10.10.1.0/24 `
        -Tags @{
            MyNote = "Public VLAN (PROD)"
        }

    Res-EC2-Subnet VPC2Subnet1 -DependsOn SampleVPC2 `
        -VpcId (Fn-Ref SampleVPC2) `
        -AvailabilityZone 10.20.1.0/24 `
        -Tags @{
            MyNote = "Public VLAN (DEV)"
        }
}

And here's the corresponding JSON that's spit out if you just execute the template as-is (provided you gave it the -JSON parameter):

{
 "AWSTemplateFormatVersion":  "2010-09-09",
 "Description":  "Sample for issue #1 request",
 "Resources":  {
       "SampleVPC1":  {
           "Type":  "AWS::EC2::VPC",
           "Properties":  {
               "CidrBlock":  "10.10.0.0/16",
               "EnableDnsSupport":  true,
               "InstanceTenancy":  "dedicated",
               "Tags":  [
                   {
                    "Key":  "MyNote",
                    "Value":  "Production network"
                   }
                  ]
              }
          },
       "SampleVPC2":  {
           "Type":  "AWS::EC2::VPC",
           "Properties":  {
               "CidrBlock":  "10.20.0.0/16",
               "Tags":  [
                   {
                    "Key":  "MyNote",
                    "Value":  "Development network"
                   }
                  ]
              }
          },
       "VPC1Subnet1":  {
            "Type":  "AWS::EC2::Subnet",
            "DependsOn":  [
               "SampleVPC1"
              ],
            "Properties":  {
                "AvailabilityZone":  "us-east-1a",
                "CidrBlock":  "10.10.1.0/24",
                "Tags":  [
                    {
                     "Key":  "MyNote",
                     "Value":  "Public VLAN (PROD)"
                    }
                   ],
                "VpcId":  "System.Collections.Hashtable"
               }
           },
       "VPC2Subnet1":  {
            "Type":  "AWS::EC2::Subnet",
            "DependsOn":  [
               "SampleVPC2"
              ],
            "Properties":  {
                "AvailabilityZone":  "10.20.1.0/24",
                "Tags":  [
                    {
                     "Key":  "MyNote",
                     "Value":  "Public VLAN (DEV)"
                    }
                   ],
                "VpcId":  "System.Collections.Hashtable"
               }
           }
      }
}
ebekker commented 7 years ago

In most cases, the properties that have a fixed list of values, such as the -InstanceTenancy property to the VPC resource, are automatically enforced and are given as options to you via IntelliSense. In this case you're restricted to one of the following values:

theonlyway commented 7 years ago

Awesome, that makes a lot more sense. I don't particularly need the cloudformation designer metadata but it's a good example if I need to ever throw that kind of information against a resource.

A few things though

  1. The module version on the PowerShell Gallery where I got this from is outdated. The gallery has version 0.8.0.4 and when I attemped to run your sample I got the following: Add-CfnEC2_VPCResource : Cannot process argument transformation on parameter 'InstanceTenancy'. Cannot convert value "dedicated" to type "System.Int32". Error: "Input string was not in a correct format." I updated to version 0.8.1.0 from this repo and it fixed that
  2. I tried to deploy your sample but got the following error from cloudformation: The vpc ID 'System.Collections.Hashtable' does not exist Which makes sense because as you can see in the JSON that gets generated it contains the type name System.Collections.Hashtable as the value for VpcId which naturally won't work. I assume it's meant to put the raw data from the command Use-CfnRefFunction -LogicalName SampleVPC2 in there and not the actual hashtable?

Either way it's makes a lot more sense now using the Use-CfnRefFunction to reference other resources in a template, just need to sort out the hashtable bit.

ebekker commented 7 years ago

Regarding (1) -- that's a great point, I never did update the version published to the gallery with the latest release. I'm working on some CI automations that should help keep this updated and more current. I'll be sure to publish the latest version to the gallery soon, as well as update to include the latest changes from the CloudFormation schema.

ebekker commented 7 years ago

Regarding (2) -- DOH! I messed up with the sample above, I mixed the "typed-resource" style and the raw cmdlet style. I'm providing the updated and corrected sample from above here:

Template -JSON -Description "Sample for issue #1 request" {

    Res-EC2-VPC SampleVPC1 -CidrBlock 10.10.0.0/16 {
        Property InstanceTenancy dedicated
        Property EnableDnsSupport $true
        Tag MyNote "Production network"
    }

    Res-EC2-VPC SampleVPC2 -CidrBlock 10.20.0.0/16 {
        Tag MyNote "Development network"
    }

    Res-EC2-Subnet VPC1Subnet1 -DependsOn SampleVPC1 {
        Property VpcId            (Fn-Ref SampleVPC1)
        Property AvailabilityZone us-east-1a
        Property CidrBlock 10.10.1.0/24
        Tag MyNote "Public VLAN (PROD)"
    }

    Res-EC2-Subnet VPC2Subnet1 -DependsOn SampleVPC2 {
        Property VpcId            (Fn-Ref SampleVPC2)
        Property AvailabilityZone 10.20.1.0/24
        Tag MyNote "Public VLAN (DEV)"
    }
}

As you can see, this is more in keeping with the idea of a declarative DSV or mini-language.

And here is the generated output:

{
 "AWSTemplateFormatVersion":  "2010-09-09",
 "Description":  "Sample for issue #1 request",
 "Resources":  {
       "SampleVPC1":  {
           "Type":  "AWS::EC2::VPC",
           "Properties":  {
               "CidrBlock":  "10.10.0.0/16",
               "InstanceTenancy":  "dedicated",
               "EnableDnsSupport":  true,
               "Tags":  [
                   {
                    "Key":  "MyNote",
                    "Value":  "Production network"
                   }
                  ]
              }
          },
       "SampleVPC2":  {
           "Type":  "AWS::EC2::VPC",
           "Properties":  {
               "CidrBlock":  "10.20.0.0/16",
               "Tags":  [
                   {
                    "Key":  "MyNote",
                    "Value":  "Development network"
                   }
                  ]
              }
          },
       "VPC1Subnet1":  {
            "Type":  "AWS::EC2::Subnet",
            "DependsOn":  [
               "SampleVPC1"
              ],
            "Properties":  {
                "VpcId":  {
                     "Ref":  "SampleVPC1"
                    },
                "AvailabilityZone":  "us-east-1a",
                "CidrBlock":  "10.10.1.0/24",
                "Tags":  [
                    {
                     "Key":  "MyNote",
                     "Value":  "Public VLAN (PROD)"
                    }
                   ]
               }
           },
       "VPC2Subnet1":  {
            "Type":  "AWS::EC2::Subnet",
            "DependsOn":  [
               "SampleVPC2"
              ],
            "Properties":  {
                "VpcId":  {
                     "Ref":  "SampleVPC2"
                    },
                "AvailabilityZone":  "10.20.1.0/24",
                "Tags":  [
                    {
                     "Key":  "MyNote",
                     "Value":  "Public VLAN (DEV)"
                    }
                   ]
               }
           }
      }
}
theonlyway commented 7 years ago

Awesome! I appreciate your time helping me with this. I'm trying to keep the stack as generic as possible so it can be re-used as a working example in the repo's wiki or something.

What I've got so far is:

$trustrelationship = @'
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "ec2.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
'@
$template = New-CfnTemplate -JSON -Description 'Sample for issue #1 request' -TemplateBlock {
  Add-CfnEC2_VPCResource -ResourceName ProdVPC -CidrBlock 10.10.0.0/16 -PropertiesBlock {
    Set-CfnResourceProperty -Name InstanceTenancy -Value default
    Set-CfnResourceProperty -Name EnableDnsSupport -Value $true
    Set-CfnResourceTag -TagKey Name -Value 'Production'
  }

  Add-CfnEC2_VPCResource -ResourceName DevVPC -CidrBlock 10.20.0.0/16 -PropertiesBlock {
    Set-CfnResourceProperty -Name InstanceTenancy -Value default
    Set-CfnResourceProperty -Name EnableDnsSupport -Value $true
    Set-CfnResourceTag -TagKey Name -Value 'Development'
  }

  Add-CfnEC2_SubnetResource -ResourceName VPC1Subnet1 -DependsOn ProdVPC -PropertiesBlock {
    Set-CfnResourceProperty -Name VpcId -Value (Use-CfnRefFunction -LogicalName ProdVPC) 
    Set-CfnResourceProperty -Name AvailabilityZone -Value ap-southeast-2a
    Set-CfnResourceProperty -Name MapPublicIpOnLaunch -Value $true
    Set-CfnResourceProperty -Name CidrBlock -Value 10.10.1.0/24
    Set-CfnResourceTag -TagKey Name -Value 'Public (PROD)'
  }

  Add-CfnEC2_SubnetResource -ResourceName VPC2Subnet1 -DependsOn DevVPC -PropertiesBlock {
    Set-CfnResourceProperty -Name VpcId -Value (Use-CfnRefFunction -LogicalName DevVPC)
    Set-CfnResourceProperty -Name CidrBlock -Value 10.20.1.0/24
    Set-CfnResourceProperty -Name AvailabilityZone -Value ap-southeast-2b
    Set-CfnResourceTag -TagKey Name -Value 'Public (DEV)'
  }

  Add-CfnEC2_SubnetResource -ResourceName VPC1Subnet2 -DependsOn ProdVPC -PropertiesBlock {
    Set-CfnResourceProperty -Name VpcId -Value (Use-CfnRefFunction -LogicalName ProdVPC) 
    Set-CfnResourceProperty -Name AvailabilityZone -Value ap-southeast-2b
    Set-CfnResourceProperty -Name MapPublicIpOnLaunch -Value $true
    Set-CfnResourceProperty -Name CidrBlock -Value 10.10.2.0/24
    Set-CfnResourceTag -TagKey Name -Value 'Private (PROD)'
  }

  Add-CfnEC2_SubnetResource -ResourceName VPC2Subnet2 -DependsOn DevVPC -PropertiesBlock {
    Set-CfnResourceProperty -Name VpcId -Value (Use-CfnRefFunction -LogicalName DevVPC)
    Set-CfnResourceProperty -Name CidrBlock -Value 10.20.2.0/24
    Set-CfnResourceProperty -Name AvailabilityZone -Value ap-southeast-2c
    Set-CfnResourceTag -TagKey Name -Value 'Private (DEV)'
  }

  Add-CfnEC2_RouteTableResource -ResourceName ProdVPCPublicRouteTable -DependsOn ProdVPC -PropertiesBlock {
    Set-CfnResourceProperty -Name VpcId -Value (Use-CfnRefFunction -LogicalName ProdVPC)
    Set-CfnResourceTag -TagKey Name -Value 'Public (PROD)'
  }

  Add-CfnEC2_RouteTableResource -ResourceName DevVPCPublicRouteTable -DependsOn DevVPC -PropertiesBlock {
    Set-CfnResourceProperty -Name VpcId -Value (Use-CfnRefFunction -LogicalName DevVPC)
    Set-CfnResourceTag -TagKey Name -Value 'Public (DEV)'
  }  

  Add-CfnEC2_RouteTableResource -ResourceName ProdVPCPrivateRouteTable -DependsOn ProdVPC -PropertiesBlock {
    Set-CfnResourceProperty -Name VpcId -Value (Use-CfnRefFunction -LogicalName ProdVPC)
    Set-CfnResourceTag -TagKey Name -Value 'Private (PROD)'
  }

  Add-CfnEC2_RouteTableResource -ResourceName DevVPCPrivateRouteTable -DependsOn DevVPC -PropertiesBlock {
    Set-CfnResourceProperty -Name VpcId -Value (Use-CfnRefFunction -LogicalName DevVPC)
    Set-CfnResourceTag -TagKey Name -Value 'Private (DEV)'
  } 

  Add-CfnEC2_SubnetRouteTableAssociationResource -ResourceName ProdVPCPublicRouteTableAssoc `
  -DependsOn ProdVPCPublicRouteTable -PropertiesBlock {
    Set-CfnResourceProperty -Name RouteTableId -Value (Use-CfnRefFunction -LogicalName ProdVPCPublicRouteTable)
    Set-CfnResourceProperty -Name SubnetId -Value (Use-CfnRefFunction -LogicalName VPC1Subnet1)      
  }

  Add-CfnEC2_SubnetRouteTableAssociationResource -ResourceName DevVPCPublicRouteTableAssoc `
  -DependsOn DevVPCPublicRouteTable -PropertiesBlock {
    Set-CfnResourceProperty -Name RouteTableId -Value (Use-CfnRefFunction -LogicalName DevVPCPublicRouteTable)
    Set-CfnResourceProperty -Name SubnetId -Value (Use-CfnRefFunction -LogicalName VPC2Subnet1)      
  }

  Add-CfnEC2_SubnetRouteTableAssociationResource -ResourceName ProdVPCPrivateRouteTableAssoc `
  -DependsOn ProdVPCPrivateRouteTable -PropertiesBlock {
    Set-CfnResourceProperty -Name RouteTableId -Value (Use-CfnRefFunction -LogicalName ProdVPCPrivateRouteTable)
    Set-CfnResourceProperty -Name SubnetId -Value (Use-CfnRefFunction -LogicalName VPC1Subnet2)      
  }

  Add-CfnEC2_SubnetRouteTableAssociationResource -ResourceName DevVPCPrivateRouteTableAssoc `
  -DependsOn DevVPCPrivateRouteTable -PropertiesBlock {
    Set-CfnResourceProperty -Name RouteTableId -Value (Use-CfnRefFunction -LogicalName DevVPCPrivateRouteTable)
    Set-CfnResourceProperty -Name SubnetId -Value (Use-CfnRefFunction -LogicalName VPC2Subnet2)      
  }

  Add-CfnEC2_InternetGatewayResource -ResourceName ProdVPCIGW -DependsOn ProdVPC -PropertiesBlock {
    Set-CfnResourceTag -TagKey Name -Value 'Production'
  }

  Add-CfnEC2_InternetGatewayResource -ResourceName DevVPCIGW -DependsOn DevVPC -PropertiesBlock {
    Set-CfnResourceTag -TagKey Name -Value 'Development'
  }

  Add-CfnEC2_VPCGatewayAttachmentResource -ResourceName ProdVPCIGWAttach -DependsOn ProdVPCIGW -PropertiesBlock {
    Set-CfnResourceProperty -Name InternetGatewayId -Value (Use-CfnRefFunction -LogicalName ProdVPCIGW)
    Set-CfnResourceProperty -Name VpcId -Value (Use-CfnRefFunction -LogicalName ProdVPC)
  }

  Add-CfnEC2_VPCGatewayAttachmentResource -ResourceName DevVPCIGWAttach -DependsOn DevVPCIGW -PropertiesBlock {
    Set-CfnResourceProperty -Name InternetGatewayId -Value (Use-CfnRefFunction -LogicalName DevVPCIGW)
    Set-CfnResourceProperty -Name VpcId -Value (Use-CfnRefFunction -LogicalName DevVPC)
  }

  Add-CfnEC2_EIPResource -ResourceName ProdVPCNGWEIP -Domain vpc
  Add-CfnEC2_EIPResource -ResourceName DevVPCNGWEIP -Domain vpc
  Add-CfnEC2_NatGatewayResource -ResourceName ProdVPCNGW -DependsOn ProdVPCNGWEIP -PropertiesBlock {
    Set-CfnResourceProperty -Name AllocationId -Value (Use-CfnGetAttFunction -ResourceName ProdVPCNGWEIP `
    -AttributeName AllocationId)
    Set-CfnResourceProperty -Name SubnetId -Value (Use-CfnRefFunction -LogicalName VPC1Subnet2)            
  }

  Add-CfnEC2_NatGatewayResource -ResourceName DevVPCNGW -DependsOn DevVPCNGWEIP -PropertiesBlock {
    Set-CfnResourceProperty -Name AllocationId -Value (Use-CfnGetAttFunction -ResourceName DevVPCNGWEIP `
    -AttributeName AllocationId)
    Set-CfnResourceProperty -Name SubnetId -Value (Use-CfnRefFunction -LogicalName VPC2Subnet2)            
  }

  Add-CfnEC2_RouteResource -ResourceName ProdVPCPublicRoute -DependsOn ProdVPCIGW -PropertiesBlock {
    Set-CfnResourceProperty -Name RouteTableId -Value (Use-CfnRefFunction -LogicalName ProdVPCPublicRouteTable)
    Set-CfnResourceProperty -Name DestinationCidrBlock -Value '0.0.0.0/0'
    Set-CfnResourceProperty -Name GatewayId -Value (Use-CfnRefFunction -LogicalName ProdVPCIGW)      
  }

  Add-CfnEC2_RouteResource -ResourceName DevVPCPublicRoute -DependsOn DevVPCIGW -PropertiesBlock {
    Set-CfnResourceProperty -Name RouteTableId -Value (Use-CfnRefFunction -LogicalName DevVPCPublicRouteTable)
    Set-CfnResourceProperty -Name DestinationCidrBlock -Value '0.0.0.0/0'
    Set-CfnResourceProperty -Name GatewayId -Value (Use-CfnRefFunction -LogicalName DevVPCIGW)      
  }

  Add-CfnEC2_RouteResource -ResourceName ProdVPCPrivateRoute -DependsOn ProdVPCNGW -PropertiesBlock {
    Set-CfnResourceProperty -Name RouteTableId -Value (Use-CfnRefFunction -LogicalName ProdVPCPrivateRouteTable)
    Set-CfnResourceProperty -Name DestinationCidrBlock -Value '0.0.0.0/0'
    Set-CfnResourceProperty -Name NatGatewayId -Value (Use-CfnRefFunction -LogicalName ProdVPCNGW)      
  }

  Add-CfnEC2_RouteResource -ResourceName DevVPCPrivateRoute -DependsOn DevVPCNGW -PropertiesBlock {
    Set-CfnResourceProperty -Name RouteTableId -Value (Use-CfnRefFunction -LogicalName DevVPCPrivateRouteTable)
    Set-CfnResourceProperty -Name DestinationCidrBlock -Value '0.0.0.0/0'
    Set-CfnResourceProperty -Name NatGatewayId -Value (Use-CfnRefFunction -LogicalName DevVPCNGW)   
  }
  Add-CfnIAM_RoleResource -ResourceName AutomationRole -AssumeRolePolicyDocument $trustrelationship -Path /
  Add-CfnIAM_InstanceProfileResource -ResourceName AutomationRoleProfile -Path / -PropertiesBlock {
    Set-CfnResourceProperty -Name Roles -Value (Use-CfnRefFunction -LogicalName AutomationRole)
  }
}

#New-CFNStack -StackName Stack1 -TemplateBody $template -Capability CAPABILITY_IAM, CAPABILITY_NAMED_IAM
Update-CFNStack -StackName Stack1 -TemplateBody $template -Capability CAPABILITY_IAM, CAPABILITY_NAMED_IAM
$template | Out-File -FilePath C:\Temp\CFjson.txt

The VPC stuff all deploys with no issues. What I'm trying to match give or take is the examples here: http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iam-role.html:

  1. I've got the AssumeRolePolicyDocument in a variable and am just passing that directly to Add-CfnIAM_RoleResource. It works but it throws in the escaping slashes in to the JSON file so I'm just wondering if there is a more ideal way to do it with a cmdlet designed to accept it?

  2. The next part is creating a instance profile for the IAM role which is basically this part: Add-CfnIAM_InstanceProfileResource -ResourceName AutomationRoleProfile -Path / -PropertiesBlock { Set-CfnResourceProperty -Name Roles -Value (Use-CfnRefFunction -LogicalName AutomationRole) } which when run in cloudformation returns the following: Value of property Roles must be of type List of String which makes sense since it's not really formatted the same as the example:


      "RootInstanceProfile": {
         "Type": "AWS::IAM::InstanceProfile",
         "Properties": {
            "Path": "/",
            "Roles": [ {
               "Ref": "RootRole"
            } ]
         }
      }
Add-CfnIAM_InstanceProfileResource -ResourceName AutomationRoleProfile -Path / -Roles (Use-CfnRefFunction -LogicalName AutomationRole)

Gets the closest but then has the same problem as above where it then attempts to output the hashtable

"AutomationRoleProfile":  {
            "Type":  "AWS::IAM::InstanceProfile",
              "Properties":  {
                  "Path":  "/",
                  "Roles":  [
                            "System.Collections.Hashtable"
                                ]
                           }
               }

Any idea how I can get it to format the InstanceProfile to match the example?

ebekker commented 7 years ago

Here is the AwsCfn version of the first example (titled "IAM Role with Embedded Policy and Instance Profiles") from that link:

Template -JSON {
    Res-IAM-Role RootRole -AssumeRolePolicyDocument @{
        Version = "2012-10-07"
        Statement = @(
            @{
                Effect = "Allow"
                Principal = @{
                    Service = @("ec2.amazonaws.com")
                }
                Action = @("sts:AssumeRole")
            }
        )
    } -Path "/" -Policies @(
        [ordered]@{
            PolicyName = "root"
            PolicyDocument = @{
                Version = "2012-10-17"
                Statement = @(
                    [ordered]@{
                        Effect   = "Allow"
                        Action   = "*"
                        Resource = "*"
                    }
                )
            }
        }
    )

    Res-IAM-InstanceProfile RootInstanceProfile -Path "/" {
        Property Roles @(
            (Fn-Ref RootRole)
        )
    }
}

And here is the JSON it produces:

{
 "AWSTemplateFormatVersion":  "2010-09-09",
 "Resources":  {
       "RootRole":  {
            "Type":  "AWS::IAM::Role",
            "Properties":  {
                "AssumeRolePolicyDocument":  {
                         "Version":  "2012-10-07",
                         "Statement":  [
                            {
                             "Effect":  "Allow",
                             "Principal":  {
                                   "Service":  [
                                       "ec2.amazonaws.com"
                                      ]
                                  },
                             "Action":  [
                                "sts:AssumeRole"
                               ]
                            }
                           ]
                        },
                "Path":  "/",
                "Policies":  [
                     {
                      "PolicyName":  "root",
                      "PolicyDocument":  {
                           "Version":  "2012-10-17",
                           "Statement":  [
                              {
                               "Effect":  "Allow",
                               "Action":  "*",
                               "Resource":  "*"
                              }
                             ]
                          }
                     }
                    ]
               }
           },
       "RootInstanceProfile":  {
              "Type":  "AWS::IAM::InstanceProfile",
              "Properties":  {
                  "Path":  "/",
                  "Roles":  [
                       {
                        "Ref":  "RootRole"
                       }
                      ]
                 }
             }
      }
}

Since the policy and policy document properties of the resource types are of type Object or Object[] it means you can supply native PowerShell types (hashtables and arrays) and they will be transformed into the appropriate JSON format when the outer template is transformed.

If the property types are more specific types, for example strings or ints or bools, then you need to use the nested property syntax as I have up above in the earlier comments, such as the following fragment which is equivalent to inline property syntax but uses nested properties instead:

Template -JSON {
    Res-IAM-Role RootRole {
        Property AssumeRolePolicyDocument @{
        Version = "2012-10-07"
        Statement = @(
            @{
                Effect = "Allow"
                Principal = @{
                    Service = @("ec2.amazonaws.com")
                }
                Action = @("sts:AssumeRole")
            }
        )
    }
theonlyway commented 7 years ago

Awesome. That makes sense now and works a treat.

One last question (hopefully!). Maybe I'm looking in the wrong places but some things appear to be missing.

Add-CfnOutput -Description 'Outputs the ID of the Prod VPC' -OutputName ProdVPCOutput `
-Value (Use-CfnRefFunction -LogicalName ProdVPC)

What I am doing is nesting stacks and what I need to do is export the VPCID and subnet ID's from the VPC stack then import them in to the EC2 stack but as far as I know I can do cross stack imports without the above.

I'm going to guess it probably ties back to issue #2?

The schema from http://vstoolkit.amazonwebservices.com/CloudFormationSchema/CloudFormationV1.schema has all of the above that I need. I assumed just throwing that in to the gen folder and running the generate scripts would be sufficient but there appears to be a bit more to it.

ebekker commented 7 years ago

You are correct, the current version is missing some of the recent enhancements to the CFN vocabulary. Some of these have only just recently been added to the V1 schema and I have not had time to incorporate them yet.

However, AWS has just announced (just yesterday, actually) that they're publishing a machine-digestible CFN Resource Specification that defines the whole of the CFN vocabulary and I'm hoping with this change they'll be releasing timely updates.

I'm working on digesting this new spec now so my plan is to have something updated very soon.

In the meantime you can actually fallback to the generic Resource directive -- it does't offer the strict type-checking but it gives you a default way to add any resource that hasn't been explicitly defined into your CFN template.

ebekker commented 7 years ago

@theonlyway -- can I ask, what version of PowerShell are you using?

theonlyway commented 7 years ago

It should be 5.1