Search code examples
pythonyamlpyyaml

Preserve a PyYaml dump


I'm trying to edit a yaml file but when I write the new file the whole structure of the file is messed up.

Line breaks are wrong, some indentations are wrong as well and it even deletes some of my parameters for some reason. Here's an example:

Before

AWSTemplateFormatVersion: "2010-09-09"
Metadata:
    Generator: "user"
Description: "CloudFormation blah blah"

Parameters:
  VPCEndpointServiceServiceName:
    Description: VPCEndpointServiceServiceName
    Type: String

Mappings:
  PrivateLink:
    EndPoint:
      EndPointName: test
      EndPointVpcId: vpc-123
      SecurityGroupIds: sg-123
      SubnetId1: subnet-123
      SubnetId2: subnet-123

Resources:
    EC2VPCEndpoint:
        Type: "AWS::EC2::VPCEndpoint"
        Properties:
            VpcEndpointType: "Interface"
            VpcId: !FindInMap [PrivateLink, EndPoint, EndPointVpcId]
            ServiceName: !Ref VPCEndpointServiceServiceName
            SubnetIds:
              - !FindInMap [PrivateLink, EndPoint, SubnetId1]
              - !FindInMap [PrivateLink, EndPoint, SubnetId2]
            PrivateDnsEnabled: false
            SecurityGroupIds:
              - !FindInMap [PrivateLink, EndPoint, SecurityGroupIds]

After

AWSTemplateFormatVersion: '2010-09-09'
Metadata:
  Generator: user
Description: CloudFormation blah blah
Parameters:
  VPCEndpointServiceServiceName:
    Description: VPCEndpointServiceServiceName
    Type: String
Mappings:
  PrivateLink:
    EndPoint:
      EndPointName: test
      EndPointVpcId: vpc-123
      SecurityGroupIds: sg-123
      SubnetId1: subnet-123
      SubnetId2: subnet-123
Resources:
  EC2VPCEndpoint:
    Type: AWS::EC2::VPCEndpoint
    Properties:
      VpcEndpointType: Interface
      VpcId:
      - PrivateLink
      - EndPoint
      - EndPointVpcId
      ServiceName: VPCEndpointServiceServiceName
      SubnetIds:
      - - PrivateLink
        - EndPoint
        - SubnetId1
      - - PrivateLink
        - EndPoint
        - SubnetId2
      PrivateDnsEnabled: 'false'
      SecurityGroupIds:
      - - PrivateLink
        - EndPoint
        - SecurityGroupIds

What's really weird is that it also removes parameters like !FindInMap and !Ref.

Here's my function:

def editEndpointTemplate(endpoint_tempplate_path):
    #read yaml file
    with open(endpoint_tempplate_path) as file:
        data = yaml.load(file, Loader=yaml.BaseLoader)

    data['Mappings']['PrivateLink']['EndPoint']['EndPointName'] = "New Name"

    #write yaml file and overwrite old one.
    with open(endpoint_tempplate_path, 'w') as file:
        yaml.dump(data, file, sort_keys=False)

I want to preserve the file exactly as it is because all I do is update a few keys.

I also had issues with the file being sorted alphabetically, but a simple sort_keys=False fixed that. I figured any other of PyYaml's attributes would solve this but couldn't figure it out.

Any help would be appreciated.


Solution

  • Found a workaround. Instead of using PyYaml or cfn-tools, I used ruamel.yaml.

    import sys
    from ruamel.yaml import YAML
    
    def editEndpointTemplate(endpoint_template_path):
        yaml = YAML()
        yaml.indent(mapping=3)
    
        #Load yaml file
        with open(endpoint_template_path) as fp:
            data = yaml.load(fp)
    
        #Change data
        data['Mappings']['PrivateLink']['EndPoint']['EndPointName'] = service_name
    
        #Dump it back to the same file
        # yaml.dump(data, sys.stdout)
        with open(endpoint_template_path, 'w') as fp:
            yaml.dump(data, fp)
    

    The new YAML file is preserved exactly like my template, even after editing.